Debugger: Support Kotlin variables for inlined lambdas inside inline functions (KT-31418)
This commit is contained in:
@@ -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))
|
||||
}
|
||||
|
||||
+3
-1
@@ -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<Loca
|
||||
return stackFrame.visibleVariables()
|
||||
}
|
||||
|
||||
// For Kotlin up to 1.3.10
|
||||
private fun lambdaOrdinalByLocalVariable(name: String): Int {
|
||||
try {
|
||||
val nameWithoutPrefix = name.removePrefix(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT)
|
||||
@@ -110,6 +111,7 @@ private fun lambdaOrdinalByLocalVariable(name: String): Int {
|
||||
}
|
||||
}
|
||||
|
||||
// For Kotlin up to 1.3.10
|
||||
private fun functionNameByLocalVariable(name: String): String {
|
||||
val nameWithoutPrefix = name.removePrefix(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT)
|
||||
return nameWithoutPrefix.substringAfterLast("$", "unknown")
|
||||
|
||||
+2
-11
@@ -129,7 +129,7 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe
|
||||
val inlineDepth = getInlineDepth(variables)
|
||||
val declarationSiteThis = variables.firstOrNull { v ->
|
||||
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
|
||||
|
||||
+9
-1
@@ -22,7 +22,6 @@ fun getInlineDepth(variables: List<LocalVariableProxyImpl>): 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)
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+27
@@ -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
|
||||
+10
@@ -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
|
||||
Generated
+5
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user