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
|
name: String, desc: String, signature: String?, start: Label, end: Label, index: Int
|
||||||
) {
|
) {
|
||||||
if (isInliningLambda || GENERATE_DEBUG_INFO) {
|
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
|
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))
|
super.visitLocalVariable(varName + varSuffix, desc, signature, start, end, getNewIndex(index))
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -58,7 +58,7 @@ fun isInsideInlineArgument(
|
|||||||
val lambdaClassName = asmTypeForAnonymousClass(bindingContext, inlineArgument)
|
val lambdaClassName = asmTypeForAnonymousClass(bindingContext, inlineArgument)
|
||||||
.internalName.substringAfterLast("/")
|
.internalName.substringAfterLast("/")
|
||||||
|
|
||||||
variableName == "-$functionName-$lambdaClassName"
|
dropInlineSuffix(variableName) == "-$functionName-$lambdaClassName"
|
||||||
} else {
|
} else {
|
||||||
// For Kotlin up to 1.3.10
|
// For Kotlin up to 1.3.10
|
||||||
lambdaOrdinalByLocalVariable(variableName) == lambdaOrdinal
|
lambdaOrdinalByLocalVariable(variableName) == lambdaOrdinal
|
||||||
@@ -100,6 +100,7 @@ private fun Location.visibleVariables(debugProcess: DebugProcessImpl): List<Loca
|
|||||||
return stackFrame.visibleVariables()
|
return stackFrame.visibleVariables()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For Kotlin up to 1.3.10
|
||||||
private fun lambdaOrdinalByLocalVariable(name: String): Int {
|
private fun lambdaOrdinalByLocalVariable(name: String): Int {
|
||||||
try {
|
try {
|
||||||
val nameWithoutPrefix = name.removePrefix(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT)
|
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 {
|
private fun functionNameByLocalVariable(name: String): String {
|
||||||
val nameWithoutPrefix = name.removePrefix(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT)
|
val nameWithoutPrefix = name.removePrefix(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT)
|
||||||
return nameWithoutPrefix.substringAfterLast("$", "unknown")
|
return nameWithoutPrefix.substringAfterLast("$", "unknown")
|
||||||
|
|||||||
+2
-11
@@ -129,7 +129,7 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe
|
|||||||
val inlineDepth = getInlineDepth(variables)
|
val inlineDepth = getInlineDepth(variables)
|
||||||
val declarationSiteThis = variables.firstOrNull { v ->
|
val declarationSiteThis = variables.firstOrNull { v ->
|
||||||
val name = v.name()
|
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) {
|
if (inlineDepth > 0 && declarationSiteThis != null) {
|
||||||
@@ -282,7 +282,7 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun LocalVariableProxyImpl.remapThisVariableIfNeeded(customName: String? = null): LocalVariableProxyImpl {
|
private fun LocalVariableProxyImpl.remapThisVariableIfNeeded(customName: String? = null): LocalVariableProxyImpl {
|
||||||
val name = this.name().dropInlineSuffix()
|
val name = dropInlineSuffix(this.name())
|
||||||
|
|
||||||
@Suppress("ConvertToStringTemplate")
|
@Suppress("ConvertToStringTemplate")
|
||||||
return when {
|
return when {
|
||||||
@@ -326,15 +326,6 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe
|
|||||||
return label
|
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 {
|
private fun LocalVariableProxyImpl.clone(name: String, label: String?): LocalVariableProxyImpl {
|
||||||
return object : LocalVariableProxyImpl(frame, variable), ThisLocalVariable {
|
return object : LocalVariableProxyImpl(frame, variable), ThisLocalVariable {
|
||||||
override fun name() = name
|
override fun name() = name
|
||||||
|
|||||||
+9
-1
@@ -22,7 +22,6 @@ fun getInlineDepth(variables: List<LocalVariableProxyImpl>): Int {
|
|||||||
if (depth > 0) {
|
if (depth > 0) {
|
||||||
return depth
|
return depth
|
||||||
} else if (name.startsWith(LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT)) {
|
} else if (name.startsWith(LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT)) {
|
||||||
// TODO this heuristics doesn't support debugging inlined lambdas inside inline functions.
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,6 +46,15 @@ fun getInlineDepth(variableName: String): Int {
|
|||||||
return depth
|
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 {
|
private fun getLocalVariableNameRegexInlineAware(name: String): Regex {
|
||||||
val escapedName = Regex.escape(name)
|
val escapedName = Regex.escape(name)
|
||||||
val escapedSuffix = Regex.escape(INLINE_FUN_VAR_SUFFIX)
|
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 = $i$f$inlineFun: int = undefined (sp = null)
|
||||||
local = element$iv$iv: double = 1.0 (sp = frameInlineArgumentInsideInlineFun.kt, 13)
|
local = element$iv$iv: double = 1.0 (sp = frameInlineArgumentInsideInlineFun.kt, 13)
|
||||||
local = it$iv: int = 1 (sp = null)
|
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
|
Disconnected from the target VM
|
||||||
|
|
||||||
Process finished with exit code 0
|
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");
|
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")
|
@TestMetadata("remapThis.kt")
|
||||||
public void testRemapThis() throws Exception {
|
public void testRemapThis() throws Exception {
|
||||||
runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/remapThis.kt");
|
runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/remapThis.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user