From 4b4d8dd66029ebd070899d0eec36e403edcca2f1 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Fri, 30 Nov 2018 18:09:57 +0900 Subject: [PATCH] Fix breakpoints for the same inline function nested calls (KT-22366) This commit changes the format of the synthetic local variables for inline functions. --- .../kotlin/codegen/FunctionCodegen.java | 32 +++++++++---------- .../idea/debugger/KotlinPositionManager.kt | 2 +- .../kotlin/idea/debugger/debuggerUtil.kt | 23 ++++++++++--- .../src/evaluate/singleBreakpoint/kt22366.kt | 14 ++++++++ .../src/evaluate/singleBreakpoint/kt22366.out | 18 +++++++++++ ...KotlinEvaluateExpressionTestGenerated.java | 5 +++ 6 files changed, 72 insertions(+), 22 deletions(-) create mode 100644 idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt22366.kt create mode 100644 idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt22366.out diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 87e5a5c2735..2b37f240ba0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -10,6 +10,7 @@ import com.intellij.psi.PsiElement; import com.intellij.util.ArrayUtil; import kotlin.collections.CollectionsKt; import kotlin.jvm.functions.Function1; +import kotlin.text.StringsKt; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.backend.common.CodegenUtil; @@ -715,26 +716,23 @@ public class FunctionCodegen { } if (context instanceof InlineLambdaContext && thisType != null && lambdaFakeIndex != -1) { - String name = thisType.getClassName(); - int indexOfLambdaOrdinal = name.lastIndexOf("$"); - if (indexOfLambdaOrdinal > 0) { - int lambdaOrdinal = Integer.parseInt(name.substring(indexOfLambdaOrdinal + 1)); + String internalName = thisType.getInternalName(); + String lambdaLocalName = StringsKt.substringAfterLast(internalName, '/', internalName); - KtPureElement functionArgument = parentCodegen.element; - String functionName = "unknown"; - if (functionArgument instanceof KtFunction) { - ValueParameterDescriptor inlineArgumentDescriptor = - InlineUtil.getInlineArgumentDescriptor((KtFunction) functionArgument, parentCodegen.bindingContext); - if (inlineArgumentDescriptor != null) { - functionName = inlineArgumentDescriptor.getContainingDeclaration().getName().asString(); - } + KtPureElement functionArgument = parentCodegen.element; + String functionName = "unknown"; + if (functionArgument instanceof KtFunction) { + ValueParameterDescriptor inlineArgumentDescriptor = + InlineUtil.getInlineArgumentDescriptor((KtFunction) functionArgument, parentCodegen.bindingContext); + if (inlineArgumentDescriptor != null) { + functionName = inlineArgumentDescriptor.getContainingDeclaration().getName().asString(); } - mv.visitLocalVariable( - JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT + lambdaOrdinal + "$" + functionName, - Type.INT_TYPE.getDescriptor(), null, - methodBegin, methodEnd, - lambdaFakeIndex); } + mv.visitLocalVariable( + JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT + "-" + functionName + "-" + lambdaLocalName, + Type.INT_TYPE.getDescriptor(), null, + methodBegin, methodEnd, + lambdaFakeIndex); } } diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt index 82d7231b7cf..6ae58fba92d 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt @@ -237,7 +237,7 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq for (literal in literalsOrFunctions) { if (InlineUtil.isInlinedArgument(literal, typeMapper.bindingContext, true)) { - if (isInsideInlineArgument(literal, location, myDebugProcess as DebugProcessImpl)) { + if (isInsideInlineArgument(literal, location, myDebugProcess as DebugProcessImpl, typeMapper.bindingContext)) { return literal } continue diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt index fe0fc89df74..a505d1efac9 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt @@ -17,6 +17,7 @@ import com.intellij.psi.PsiElement import com.sun.jdi.* import com.sun.tools.jdi.LocalVariableImpl import org.jetbrains.kotlin.codegen.binding.CodegenBinding +import org.jetbrains.kotlin.codegen.binding.CodegenBinding.asmTypeForAnonymousClass import org.jetbrains.kotlin.codegen.coroutines.DO_RESUME_METHOD_NAME import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME import org.jetbrains.kotlin.codegen.coroutines.continuationAsmTypes @@ -123,7 +124,12 @@ fun numberOfInlinedFunctions(visibleVariables: List): Int { return visibleVariables.count { it.name().startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) } } -fun isInsideInlineArgument(inlineArgument: KtFunction, location: Location, debugProcess: DebugProcessImpl): Boolean { +fun isInsideInlineArgument( + inlineArgument: KtFunction, + location: Location, + debugProcess: DebugProcessImpl, + bindingContext: BindingContext = KotlinDebuggerCaches.getOrCreateTypeMapper(inlineArgument).bindingContext +): Boolean { val visibleVariables = location.visibleVariables(debugProcess) val markerLocalVariables = visibleVariables.filter { it.name().startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT) } @@ -132,10 +138,19 @@ fun isInsideInlineArgument(inlineArgument: KtFunction, location: Location, debug val functionName = runReadAction { functionNameByArgument(inlineArgument, context) } return markerLocalVariables - .map { it.name() } - .any { variableName -> - lambdaOrdinalByLocalVariable(variableName) == lambdaOrdinal && functionNameByLocalVariable(variableName) == functionName + .map { it.name().drop(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT.length) } + .any { variableName -> + if (variableName.startsWith("-")) { + val lambdaClassName = asmTypeForAnonymousClass(bindingContext, inlineArgument) + .internalName.substringAfterLast("/") + + variableName == "-$functionName-$lambdaClassName" + } else { + // For Kotlin up to 1.3.10 + lambdaOrdinalByLocalVariable(variableName) == lambdaOrdinal + && functionNameByLocalVariable(variableName) == functionName } + } } fun DebugProcessImpl.invokeInManagerThread(f: (DebuggerContextImpl) -> T?): T? { diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt22366.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt22366.kt new file mode 100644 index 00000000000..2fa166e173f --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt22366.kt @@ -0,0 +1,14 @@ +package kt22366 + +fun main(args: Array) { + val yy = (0 .. 10).map { a -> + //Breakpoint! (lambdaOrdinal = -1) + (0 .. 10).map { b -> b }.max() + } +} + +// EXPRESSION: a +// RESULT: 0: I + +// EXPRESSION: b +// RESULT: Unresolved reference: b \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt22366.out b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt22366.out new file mode 100644 index 00000000000..d4acb311695 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt22366.out @@ -0,0 +1,18 @@ +LineBreakpoint created at kt22366.kt:6 lambdaOrdinal = -1 +Run Java +Connected to the target VM +kt22366.kt:6 +Compile bytecode for a +kt22366.kt:6 +resuming kt22366.kt:6 +resuming kt22366.kt:6 +resuming kt22366.kt:6 +resuming kt22366.kt:6 +resuming kt22366.kt:6 +resuming kt22366.kt:6 +resuming kt22366.kt:6 +resuming kt22366.kt:6 +resuming kt22366.kt:6 +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 c9c283c21a5..2041ef906e5 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -226,6 +226,11 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt17514.kt"); } + @TestMetadata("kt22366.kt") + public void testKt22366() throws Exception { + runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt22366.kt"); + } + @TestMetadata("kt5554OnlyIntsShouldBeCoerced.kt") public void testKt5554OnlyIntsShouldBeCoerced() throws Exception { runTest("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/kt5554OnlyIntsShouldBeCoerced.kt");