diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinStackFrame.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinStackFrame.kt index 8350dba2353..0ef056c1905 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinStackFrame.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinStackFrame.kt @@ -30,12 +30,12 @@ import com.sun.jdi.ReferenceType import com.sun.jdi.Type import com.sun.jdi.Value import org.jetbrains.kotlin.codegen.AsmUtil +import org.jetbrains.kotlin.codegen.AsmUtil.THIS import org.jetbrains.kotlin.codegen.DESTRUCTURED_LAMBDA_ARGUMENT_VARIABLE_PREFIX -import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_PARAMETER_NAME +import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_VARIABLE_NAME import org.jetbrains.kotlin.codegen.inline.INLINE_FUN_VAR_SUFFIX import org.jetbrains.kotlin.codegen.inline.isFakeLocalVariableForInline -import org.jetbrains.kotlin.idea.debugger.evaluate.THIS_NAME -import org.jetbrains.kotlin.idea.debugger.evaluate.VariableFinder +import org.jetbrains.kotlin.idea.debugger.evaluate.variables.VariableFinder import org.jetbrains.kotlin.utils.getSafe import java.lang.reflect.Modifier import java.util.* @@ -57,9 +57,9 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe } val (thisReferences, otherVariables) = visibleVariables - .partition { it.name() == AsmUtil.THIS || it is ThisLocalVariable } + .partition { it.name() == THIS || it is ThisLocalVariable } - if (!removeThisObjectForContinuation(evaluationContext, children) && thisReferences.isNotEmpty()) { + if (!removeSyntheticThisObject(evaluationContext, children) && thisReferences.isNotEmpty()) { val thisLabels = thisReferences.asSequence() .filterIsInstance() .mapNotNullTo(hashSetOf()) { it.label } @@ -71,14 +71,22 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe otherVariables.forEach(::addItem) } - private fun removeThisObjectForContinuation(evaluationContext: EvaluationContextImpl, children: XValueChildrenList): Boolean { + private fun removeSyntheticThisObject(evaluationContext: EvaluationContextImpl, children: XValueChildrenList): Boolean { val thisObject = evaluationContext.frameProxy?.thisObject() ?: return false - if (!thisObject.type().isSubtype(VariableFinder.CONTINUATION_TYPE)) { - return false + + + if (thisObject.type().isSubtype(VariableFinder.CONTINUATION_TYPE)) { + ExistingInstanceThis.find(children)?.remove() + return true } - ExistingInstanceThis.find(children)?.remove() - return true + val thisObjectType = thisObject.type() + if (thisObjectType.isSubtype(Function::class.java.name) && '$' in thisObjectType.signature()) { + ExistingInstanceThis.find(children)?.remove() + return true + } + + return false } private fun remapThisObjectForOuterThis( @@ -190,7 +198,8 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe val (thisVariables, otherVariables) = allVisibleVariables.asSequence() .filter { !isHidden(it, inlineDepth) } .partition { - it.name() == AsmUtil.THIS + it.name() == THIS + || it.name() == AsmUtil.getCapturedFieldName(AsmUtil.THIS) || it.name().startsWith(AsmUtil.LABELED_THIS_PARAMETER) || (VariableFinder.inlinedThisRegex.matches(it.name())) } @@ -199,7 +208,7 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe .sortedByDescending { it.variable } .let { it.firstOrNull() to it.drop(1) } - val remappedMainThis = mainThis?.clone(AsmUtil.THIS, null) + val remappedMainThis = mainThis?.clone(THIS, null) val remappedOther = (otherThis + otherVariables).map { it.remapVariableNameIfNeeded() } return (listOfNotNull(remappedMainThis) + remappedOther).sortedBy { it.variable } } @@ -216,12 +225,14 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe private fun LocalVariableProxyImpl.remapVariableNameIfNeeded(): LocalVariableProxyImpl { val name = this.name().dropInlineSuffix() + @Suppress("ConvertToStringTemplate") return when { isLabeledThisReference() -> { val label = name.drop(AsmUtil.LABELED_THIS_PARAMETER.length) clone(getThisName(label), label) } - name == AsmUtil.RECEIVER_PARAMETER_NAME -> clone(AsmUtil.THIS + " (receiver)", null) + name == AsmUtil.getCapturedFieldName(AsmUtil.THIS) -> clone(THIS + " (outer)", null) + name == AsmUtil.RECEIVER_PARAMETER_NAME -> clone(THIS + " (receiver)", null) VariableFinder.inlinedThisRegex.matches(name) -> { val label = generateThisLabel(frame.getValue(this)?.type()) if (label != null) { @@ -260,7 +271,7 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe } private fun getThisName(label: String): String { - return "$THIS_NAME (@$label)" + return "$THIS (@$label)" } private fun LocalVariableProxyImpl.clone(name: String, label: String?): LocalVariableProxyImpl { diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/defaultImplsMangling.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/defaultImplsMangling.kt new file mode 100644 index 00000000000..80ed96aec6a --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/defaultImplsMangling.kt @@ -0,0 +1,24 @@ +package defaultImplsMangling + +interface IFoo { + val x: Int + + fun foo() { + //Breakpoint! + val a = 5 + } +} + +class Foo : IFoo { + override val x: Int = 1 +} + +fun main() { + Foo().foo() +} + +// SHOW_KOTLIN_VARIABLES +// PRINT_FRAME + +// EXPRESSION: x +// RESULT: 1: I \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/defaultImplsMangling.out b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/defaultImplsMangling.out new file mode 100644 index 00000000000..0fdb80e59e0 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/defaultImplsMangling.out @@ -0,0 +1,11 @@ +LineBreakpoint created at defaultImplsMangling.kt:8 +Run Java +Connected to the target VM +defaultImplsMangling.kt:8 +Compile bytecode for x + frame = foo:8, IFoo$DefaultImpls {defaultImplsMangling} + local = this: defaultImplsMangling.IFoo = {defaultImplsMangling.Foo@uniqueID} (sp = null) + field = x: int = 1 (sp = defaultImplsMangling.kt, 13) +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/lambdaThisMangling.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/lambdaThisMangling.kt new file mode 100644 index 00000000000..8d7afee29ca --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/lambdaThisMangling.kt @@ -0,0 +1,18 @@ +package lambdaThisMangling + +private fun block(block: (String) -> Unit) { + block("foo") +} + +fun main() { + block { foo -> + //Breakpoint! + val a = 5 + } +} + +// SHOW_KOTLIN_VARIABLES +// PRINT_FRAME + +// EXPRESSION: foo +// RESULT: "foo": Ljava/lang/String; \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/lambdaThisMangling.out b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/lambdaThisMangling.out new file mode 100644 index 00000000000..10862b68105 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/lambdaThisMangling.out @@ -0,0 +1,15 @@ +LineBreakpoint created at lambdaThisMangling.kt:10 +Run Java +Connected to the target VM +lambdaThisMangling.kt:10 +Compile bytecode for foo + frame = invoke:10, LambdaThisManglingKt$main$1 {lambdaThisMangling} + local = foo: java.lang.String = foo (sp = lambdaThisMangling.kt, 8) + field = value: char[] = {char[3]@uniqueID} (sp = String.!EXT!) + element = 0 = 'f' 102 + element = 1 = 'o' 111 + element = 2 = 'o' 111 + field = hash: int = 0 (sp = String.!EXT!) +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java index e3fa0a3fd3d..c2392e9817d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -620,6 +620,11 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/coroutineContextLambda.kt"); } + @TestMetadata("defaultImplsMangling.kt") + public void testDefaultImplsMangling() throws Exception { + runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/defaultImplsMangling.kt"); + } + @TestMetadata("delegatedPropertyInClass.kt") public void testDelegatedPropertyInClass() throws Exception { runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/delegatedPropertyInClass.kt"); @@ -760,6 +765,11 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/lambdaParameterMangling.kt"); } + @TestMetadata("lambdaThisMangling.kt") + public void testLambdaThisMangling() throws Exception { + runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/lambdaThisMangling.kt"); + } + @TestMetadata("localFunctionMangling.kt") public void testLocalFunctionMangling() throws Exception { runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/localFunctionMangling.kt");