From b4f515a4366b910b0242b608994979b635c27422 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Wed, 22 May 2019 17:27:23 +0900 Subject: [PATCH] Debugger: Support Kotlin variables for inlined lambdas inside inline functions (KT-31418) --- .../kotlin/codegen/inline/MethodInliner.kt | 7 ++++- .../kotlin/idea/debugger/debuggerUtil.kt | 4 ++- .../debugger/stackFrame/KotlinStackFrame.kt | 13 ++------- .../kotlin/idea/debugger/InlineUtils.kt | 10 ++++++- .../frameInlineArgumentInsideInlineFun.out | 2 +- .../frame/nestedInlineFun2.kt | 27 +++++++++++++++++++ .../frame/nestedInlineFun2.out | 10 +++++++ ...KotlinEvaluateExpressionTestGenerated.java | 5 ++++ 8 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/nestedInlineFun2.kt create mode 100644 idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/nestedInlineFun2.out diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt index 95ebb113869..7c29493bf95 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt @@ -487,7 +487,12 @@ class MethodInliner( name: String, desc: String, signature: String?, start: Label, end: Label, index: Int ) { if (isInliningLambda || GENERATE_DEBUG_INFO) { - val varSuffix = if (inliningContext.isRoot && !isFakeLocalVariableForInline(name)) INLINE_FUN_VAR_SUFFIX else "" + val isInlineFunctionMarker = name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) + val varSuffix = when { + inliningContext.isRoot && !isInlineFunctionMarker -> INLINE_FUN_VAR_SUFFIX + else -> "" + } + val varName = if (!varSuffix.isEmpty() && name == AsmUtil.THIS) AsmUtil.INLINE_DECLARATION_SITE_THIS else name super.visitLocalVariable(varName + varSuffix, desc, signature, start, end, getNewIndex(index)) } diff --git a/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt b/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt index debd09bd7eb..dd7df5eb7ab 100644 --- a/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt +++ b/idea/jvm-debugger/jvm-debugger-core/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt @@ -58,7 +58,7 @@ fun isInsideInlineArgument( val lambdaClassName = asmTypeForAnonymousClass(bindingContext, inlineArgument) .internalName.substringAfterLast("/") - variableName == "-$functionName-$lambdaClassName" + dropInlineSuffix(variableName) == "-$functionName-$lambdaClassName" } else { // For Kotlin up to 1.3.10 lambdaOrdinalByLocalVariable(variableName) == lambdaOrdinal @@ -100,6 +100,7 @@ private fun Location.visibleVariables(debugProcess: DebugProcessImpl): List val name = v.name() - name.endsWith(INLINE_FUN_VAR_SUFFIX) && name.dropInlineSuffix() == AsmUtil.INLINE_DECLARATION_SITE_THIS + name.endsWith(INLINE_FUN_VAR_SUFFIX) && dropInlineSuffix(name) == AsmUtil.INLINE_DECLARATION_SITE_THIS } if (inlineDepth > 0 && declarationSiteThis != null) { @@ -282,7 +282,7 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe } private fun LocalVariableProxyImpl.remapThisVariableIfNeeded(customName: String? = null): LocalVariableProxyImpl { - val name = this.name().dropInlineSuffix() + val name = dropInlineSuffix(this.name()) @Suppress("ConvertToStringTemplate") return when { @@ -326,15 +326,6 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe return label } - private fun String.dropInlineSuffix(): String { - val depth = getInlineDepth(this) - if (depth == 0) { - return this - } - - return dropLast(depth * INLINE_FUN_VAR_SUFFIX.length) - } - private fun LocalVariableProxyImpl.clone(name: String, label: String?): LocalVariableProxyImpl { return object : LocalVariableProxyImpl(frame, variable), ThisLocalVariable { override fun name() = name diff --git a/idea/jvm-debugger/jvm-debugger-util/src/org/jetbrains/kotlin/idea/debugger/InlineUtils.kt b/idea/jvm-debugger/jvm-debugger-util/src/org/jetbrains/kotlin/idea/debugger/InlineUtils.kt index 229339d875c..04323f6caeb 100644 --- a/idea/jvm-debugger/jvm-debugger-util/src/org/jetbrains/kotlin/idea/debugger/InlineUtils.kt +++ b/idea/jvm-debugger/jvm-debugger-util/src/org/jetbrains/kotlin/idea/debugger/InlineUtils.kt @@ -22,7 +22,6 @@ fun getInlineDepth(variables: List): Int { if (depth > 0) { return depth } else if (name.startsWith(LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT)) { - // TODO this heuristics doesn't support debugging inlined lambdas inside inline functions. return 0 } } @@ -47,6 +46,15 @@ fun getInlineDepth(variableName: String): Int { return depth } +fun dropInlineSuffix(name: String): String { + val depth = getInlineDepth(name) + if (depth == 0) { + return name + } + + return name.dropLast(depth * INLINE_FUN_VAR_SUFFIX.length) +} + private fun getLocalVariableNameRegexInlineAware(name: String): Regex { val escapedName = Regex.escape(name) val escapedSuffix = Regex.escape(INLINE_FUN_VAR_SUFFIX) diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/frameInlineArgumentInsideInlineFun.out b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/frameInlineArgumentInsideInlineFun.out index 3b71ea69b79..43f7537ec5b 100644 --- a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/frameInlineArgumentInsideInlineFun.out +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/frameInlineArgumentInsideInlineFun.out @@ -16,7 +16,7 @@ Compile bytecode for element local = $i$f$inlineFun: int = undefined (sp = null) local = element$iv$iv: double = 1.0 (sp = frameInlineArgumentInsideInlineFun.kt, 13) local = it$iv: int = 1 (sp = null) - local = $i$a$-inlineFun-B$foo$1: int = undefined (sp = null) + local = $i$a$-inlineFun-B$foo$1$iv: int = undefined (sp = null) Disconnected from the target VM Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/nestedInlineFun2.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/nestedInlineFun2.kt new file mode 100644 index 00000000000..8bf402b7913 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/nestedInlineFun2.kt @@ -0,0 +1,27 @@ +package nestedInlineFun2 + +fun main() { + val a = 1 + foo { + val b = 2 + } +} + +inline fun foo(block: () -> Unit) { + val x = 3 + bar(1) { + val y = 2 + //Breakpoint! + block() + } +} + +inline fun bar(count: Int, block: () -> Unit) { + var i = count + while (i-- > 0) { + block() + } +} + +// SHOW_KOTLIN_VARIABLES +// PRINT_FRAME \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/nestedInlineFun2.out b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/nestedInlineFun2.out new file mode 100644 index 00000000000..5932b169c85 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/nestedInlineFun2.out @@ -0,0 +1,10 @@ +LineBreakpoint created at nestedInlineFun2.kt:15 +Run Java +Connected to the target VM +nestedInlineFun2.kt:15 + frame = main:15, NestedInlineFun2Kt {nestedInlineFun2} + local = x: int = 3 (sp = nestedInlineFun2.kt, 11) + local = y: int = 2 (sp = nestedInlineFun2.kt, 13) +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 5b147240530..6463c87b557 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -905,6 +905,11 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/nestedInlineFun.kt"); } + @TestMetadata("nestedInlineFun2.kt") + public void testNestedInlineFun2() throws Exception { + runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/nestedInlineFun2.kt"); + } + @TestMetadata("remapThis.kt") public void testRemapThis() throws Exception { runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/remapThis.kt");