From 2aaa94cccbc5de899671de02ef82f5bb40a135f4 Mon Sep 17 00:00:00 2001 From: Natalia Ukhorskaya Date: Fri, 26 Dec 2014 15:48:42 +0300 Subject: [PATCH] Debugger: implement Jump to Source for localVariables --- .../debugger/KotlinSourcePositionProvider.kt | 49 +++++++++++++++++++ .../tinyApp/outs/delegatedPropertyInClass.out | 4 +- .../tinyApp/outs/frameClosingBracket.out | 4 +- .../tinyApp/outs/frameExtensionFun.out | 2 +- .../tinyApp/outs/frameLambdaNotUsed.out | 2 +- .../tinyApp/outs/frameSharedVarLocalVar.out | 4 +- .../debugger/tinyApp/outs/frameSimple.out | 6 +-- .../debugger/tinyApp/outs/frameThis0.out | 2 +- .../tinyApp/outs/toStringRenderer.out | 4 +- .../AbstractKotlinEvaluateExpressionTest.kt | 3 +- 10 files changed, 65 insertions(+), 15 deletions(-) diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/KotlinSourcePositionProvider.kt b/idea/src/org/jetbrains/jet/plugin/debugger/KotlinSourcePositionProvider.kt index cf70d1d03df..72d9b4c7589 100644 --- a/idea/src/org/jetbrains/jet/plugin/debugger/KotlinSourcePositionProvider.kt +++ b/idea/src/org/jetbrains/jet/plugin/debugger/KotlinSourcePositionProvider.kt @@ -33,6 +33,19 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import com.sun.jdi.AbsentInformationException import com.sun.jdi.ClassNotPreparedException import com.intellij.debugger.ui.tree.FieldDescriptor +import com.intellij.debugger.ui.tree.LocalVariableDescriptor +import com.intellij.debugger.impl.PositionUtil +import com.intellij.psi.PsiVariable +import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.psi.JetElement +import org.jetbrains.jet.plugin.caches.resolve.analyze +import org.jetbrains.kotlin.psi.JetPsiFactory +import org.jetbrains.kotlin.resolve.BindingContext +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.psi.JetSimpleNameExpression +import org.jetbrains.kotlin.resolve.BindingContextUtils +import org.jetbrains.kotlin.resolve.source.KotlinSourceElement +import org.jetbrains.kotlin.resolve.source.getPsi public class KotlinSourcePositionProvider: SourcePositionProvider() { override fun computeSourcePosition(descriptor: NodeDescriptor, project: Project, context: DebuggerContextImpl, nearest: Boolean): SourcePosition? { @@ -42,6 +55,42 @@ public class KotlinSourcePositionProvider: SourcePositionProvider() { return computeSourcePosition(descriptor, project, context, nearest) } + if (descriptor is LocalVariableDescriptor) { + return computeSourcePosition(descriptor, project, context, nearest) + } + + return null + } + + fun computeSourcePosition(descriptor: LocalVariableDescriptor, project: Project, context: DebuggerContextImpl, nearest: Boolean): SourcePosition? { + val place = PositionUtil.getContextElement(context) + if (place == null) { + return null + } + + val contextElement = PsiTreeUtil.getParentOfType(place, javaClass()) + if (contextElement == null) { + return null + } + + val codeFragment = JetPsiFactory(project).createExpressionCodeFragment(descriptor.getName(), contextElement) + val expression = codeFragment.getContentElement() + if (expression is JetSimpleNameExpression) { + val bindingContext = expression.analyze() + val declarationDescriptor = BindingContextUtils.extractVariableDescriptorIfAny(bindingContext, expression, false) + val sourceElement = declarationDescriptor?.getSource() + if (sourceElement is KotlinSourceElement) { + val element = sourceElement.getPsi() + if (element == null) { + return null + } + if (nearest) { + return DebuggerContextUtil.findNearest(context, element, element.getContainingFile()) + } + return SourcePosition.createFromOffset(element.getContainingFile(), element.getTextOffset()) + } + } + return null } diff --git a/idea/testData/debugger/tinyApp/outs/delegatedPropertyInClass.out b/idea/testData/debugger/tinyApp/outs/delegatedPropertyInClass.out index b0e66be3177..ea36113f228 100644 --- a/idea/testData/debugger/tinyApp/outs/delegatedPropertyInClass.out +++ b/idea/testData/debugger/tinyApp/outs/delegatedPropertyInClass.out @@ -28,8 +28,8 @@ class MyDelegateThrowsException { // PRINT_FRAME frame = main():8, DelegatedPropertyInClassPackage$@packagePartHASH {delegatedPropertyInClass} static = static = delegatedPropertyInClass.DelegatedPropertyInClassPackage$@packagePartHASH - local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} - local = a: delegatedPropertyInClass.A = {delegatedPropertyInClass.A@uniqueID} + local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = delegatedPropertyInClass.kt, 5) + local = a: delegatedPropertyInClass.A = {delegatedPropertyInClass.A@uniqueID} (sp = delegatedPropertyInClass.kt, 6) field = prop$delegate: delegatedPropertyInClass.MyDelegate = {delegatedPropertyInClass.MyDelegate@uniqueID} (sp = delegatedPropertyInClass.kt, 12) field = prop: int = 1 (sp = delegatedPropertyInClass.kt, 12) field = propEx$delegate: delegatedPropertyInClass.MyDelegateThrowsException = {delegatedPropertyInClass.MyDelegateThrowsException@uniqueID} (sp = delegatedPropertyInClass.kt, 13) diff --git a/idea/testData/debugger/tinyApp/outs/frameClosingBracket.out b/idea/testData/debugger/tinyApp/outs/frameClosingBracket.out index 1f20949c1c5..3c74dec28b0 100644 --- a/idea/testData/debugger/tinyApp/outs/frameClosingBracket.out +++ b/idea/testData/debugger/tinyApp/outs/frameClosingBracket.out @@ -16,8 +16,8 @@ fun main(args: Array) { frame = main():6, FrameClosingBracketPackage$@packagePartHASH {frameClosingBracket} static = static = frameClosingBracket.FrameClosingBracketPackage$@packagePartHASH - local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} - local = a: int = 1 + local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = frameClosingBracket.kt, 3) + local = a: int = 1 (sp = frameClosingBracket.kt, 4) Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/outs/frameExtensionFun.out b/idea/testData/debugger/tinyApp/outs/frameExtensionFun.out index 94c47b16492..057cf614332 100644 --- a/idea/testData/debugger/tinyApp/outs/frameExtensionFun.out +++ b/idea/testData/debugger/tinyApp/outs/frameExtensionFun.out @@ -24,7 +24,7 @@ fun A.foo() { // RESULT: 1: I frame = foo():13, FrameExtensionFunPackage$@packagePartHASH {frameExtensionFun} static = static = frameExtensionFun.FrameExtensionFunPackage$@packagePartHASH - local = $receiver: frameExtensionFun.A = {frameExtensionFun.A@uniqueID} + local = $receiver: frameExtensionFun.A = {frameExtensionFun.A@uniqueID} (sp = null) field = prop: int = 1 (sp = frameExtensionFun.kt, 8) Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' diff --git a/idea/testData/debugger/tinyApp/outs/frameLambdaNotUsed.out b/idea/testData/debugger/tinyApp/outs/frameLambdaNotUsed.out index d74fc1c2e3b..cc8710e395f 100644 --- a/idea/testData/debugger/tinyApp/outs/frameLambdaNotUsed.out +++ b/idea/testData/debugger/tinyApp/outs/frameLambdaNotUsed.out @@ -23,7 +23,7 @@ fun foo(f: () -> Unit) { // RESULT: Cannot find local variable: name = val1 frame = invoke():7, FrameLambdaNotUsedPackage$@packagePartHASH$main$1 {frameLambdaNotUsed} this = this = {frameLambdaNotUsed.FrameLambdaNotUsedPackage$@packagePartHASH$main$1@uniqueID}kotlin.Function0 - local = a: int = 0 + local = a: int = 0 (sp = frameLambdaNotUsed.kt, 7) Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/outs/frameSharedVarLocalVar.out b/idea/testData/debugger/tinyApp/outs/frameSharedVarLocalVar.out index cf6308984a9..a56691580cb 100644 --- a/idea/testData/debugger/tinyApp/outs/frameSharedVarLocalVar.out +++ b/idea/testData/debugger/tinyApp/outs/frameSharedVarLocalVar.out @@ -23,8 +23,8 @@ inline fun foo(f: () -> Unit) { // RESULT: 1: I frame = main():7, FrameSharedVarLocalVarPackage$@packagePartHASH {frameSharedVarLocalVar} static = static = frameSharedVarLocalVar.FrameSharedVarLocalVarPackage$@packagePartHASH - local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} - local = var1: kotlin.jvm.internal.Ref$IntRef = {kotlin.jvm.internal.Ref$IntRef@uniqueID}1 + local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = frameSharedVarLocalVar.kt, 3) + local = var1: kotlin.jvm.internal.Ref$IntRef = {kotlin.jvm.internal.Ref$IntRef@uniqueID}1 (sp = frameSharedVarLocalVar.kt, 4) field = element: int = 1 (sp = Ref.!EXT!) Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' diff --git a/idea/testData/debugger/tinyApp/outs/frameSimple.out b/idea/testData/debugger/tinyApp/outs/frameSimple.out index 25e7d196c03..e98f3c7c341 100644 --- a/idea/testData/debugger/tinyApp/outs/frameSimple.out +++ b/idea/testData/debugger/tinyApp/outs/frameSimple.out @@ -33,9 +33,9 @@ fun main(args: Array) { frame = main():9, FrameSimplePackage$@packagePartHASH {frameSimple} static = static = frameSimple.FrameSimplePackage$@packagePartHASH field = topVal1: int = 1 (sp = null) - local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} - local = val1: int = 1 - local = val2: java.lang.String = {@uniqueID}str + local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = frameSimple.kt, 5) + local = val1: int = 1 (sp = frameSimple.kt, 6) + local = val2: java.lang.String = {@uniqueID}str (sp = frameSimple.kt, 7) field = value: char[] = {char[3]@uniqueID} (sp = String.!EXT!) unknown = [0] = 's' 115 unknown = [1] = 't' 116 diff --git a/idea/testData/debugger/tinyApp/outs/frameThis0.out b/idea/testData/debugger/tinyApp/outs/frameThis0.out index 619e479045d..cafd9dc3165 100644 --- a/idea/testData/debugger/tinyApp/outs/frameThis0.out +++ b/idea/testData/debugger/tinyApp/outs/frameThis0.out @@ -47,7 +47,7 @@ fun foo(f: () -> Unit) { field = this$0: frameThis0.A = {frameThis0.A@uniqueID} (sp = null) field = prop1: int = 1 (sp = frameThis0.kt, 8) field = $val1: int = 1 (sp = null) - local = val2: int = 1 + local = val2: int = 1 (sp = frameThis0.kt, 13) Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/outs/toStringRenderer.out b/idea/testData/debugger/tinyApp/outs/toStringRenderer.out index e9d17b3a972..75274c6c408 100644 --- a/idea/testData/debugger/tinyApp/outs/toStringRenderer.out +++ b/idea/testData/debugger/tinyApp/outs/toStringRenderer.out @@ -17,8 +17,8 @@ class A { // PRINT_FRAME frame = main():6, ToStringRendererPackage$@packagePartHASH {toStringRenderer} static = static = toStringRenderer.ToStringRendererPackage$@packagePartHASH - local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} - local = a: toStringRenderer.A = {toStringRenderer.A@uniqueID}myA + local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = toStringRenderer.kt, 3) + local = a: toStringRenderer.A = {toStringRenderer.A@uniqueID}myA (sp = toStringRenderer.kt, 4) Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Process finished with exit code 0 diff --git a/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt b/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt index 34717ba5253..d5f9d8e5c7d 100644 --- a/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt +++ b/idea/tests/org/jetbrains/jet/plugin/debugger/evaluate/AbstractKotlinEvaluateExpressionTest.kt @@ -250,7 +250,8 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestB val curIndent = " ".repeat(indent) when (descriptor) { is StackFrameDescriptor -> logDescriptor(descriptor, "$curIndent frame = $label\n") - is LocalVariableDescriptor -> logDescriptor(descriptor, "$curIndent local = $label\n") + is LocalVariableDescriptor -> logDescriptor(descriptor, "$curIndent local = $label" + + " (sp = ${render(SourcePositionProvider.getSourcePosition(descriptor, myProject, debuggerContext))})\n") is StaticDescriptor -> logDescriptor(descriptor, "$curIndent static = $label\n") is ThisDescriptorImpl -> logDescriptor(descriptor, "$curIndent this = $label\n") is FieldDescriptor -> logDescriptor(descriptor, "$curIndent field = $label"