Skip to content

Commit 0fb5a18

Browse files
NataliaUkhorskayagoodwinnk
authored andcommitted
Debugger: refactor smart step into to use descriptors instead of psi elements (KT-13485)
#KT-13485 Fixed
1 parent f7203da commit 0fb5a18

File tree

10 files changed

+162
-67
lines changed

10 files changed

+162
-67
lines changed

idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ import org.jetbrains.kotlin.idea.util.application.runReadAction
5050
import org.jetbrains.kotlin.load.java.JvmAbi
5151
import org.jetbrains.kotlin.name.FqName
5252
import org.jetbrains.kotlin.psi.KtClass
53+
import org.jetbrains.kotlin.psi.KtElement
5354
import org.jetbrains.kotlin.psi.KtFile
5455
import org.jetbrains.kotlin.psi.KtFunction
55-
import org.jetbrains.kotlin.psi.KtParameter
5656
import org.jetbrains.kotlin.resolve.inline.InlineUtil
5757
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
5858
import java.util.*
@@ -116,9 +116,9 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
116116
if (lambdaOrFunIfInside != null) {
117117
return SourcePosition.createFromElement(lambdaOrFunIfInside.bodyExpression!!)
118118
}
119-
val property = getParameterIfInConstructor(location, psiFile, lineNumber)
120-
if (property != null) {
121-
return SourcePosition.createFromElement(property)
119+
val elementInDeclaration = getElementForDeclarationLine(location, psiFile, lineNumber)
120+
if (elementInDeclaration != null) {
121+
return SourcePosition.createFromElement(elementInDeclaration)
122122
}
123123

124124
if (lineNumber > psiFile.getLineCount() && myDebugProcess.isDexDebug()) {
@@ -134,18 +134,25 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
134134
return SourcePosition.createFromLine(psiFile, lineNumber)
135135
}
136136

137-
private fun getParameterIfInConstructor(location: Location, file: KtFile, lineNumber: Int): KtParameter? {
137+
// Returns a property or a constructor if debugger stops at class declaration
138+
private fun getElementForDeclarationLine(location: Location, file: KtFile, lineNumber: Int): KtElement? {
138139
val lineStartOffset = file.getLineStartOffset(lineNumber) ?: return null
139140
val elementAt = file.findElementAt(lineStartOffset)
140141
val contextElement = KotlinCodeFragmentFactory.getContextElement(elementAt)
142+
143+
if (contextElement !is KtClass) return null
144+
141145
val methodName = location.method().name()
142-
if (contextElement is KtClass && JvmAbi.isGetterName(methodName)) {
143-
val parameterForGetter = contextElement.getPrimaryConstructor()?.valueParameters?.firstOrNull() {
144-
it.hasValOrVar() && it.name != null && JvmAbi.getterName(it.name!!) == methodName
145-
} ?: return null
146-
return parameterForGetter
146+
return when {
147+
JvmAbi.isGetterName(methodName) -> {
148+
val parameterForGetter = contextElement.getPrimaryConstructor()?.valueParameters?.firstOrNull() {
149+
it.hasValOrVar() && it.name != null && JvmAbi.getterName(it.name!!) == methodName
150+
} ?: return null
151+
parameterForGetter
152+
}
153+
methodName == "<init>" -> contextElement.getPrimaryConstructor()
154+
else -> null
147155
}
148-
return null
149156
}
150157

151158
private fun getLambdaOrFunIfInside(location: Location, file: KtFile, lineNumber: Int): KtFunction? {

idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinBasicStepMethodFilter.kt

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,31 @@
1616

1717
package org.jetbrains.kotlin.idea.debugger.stepping
1818

19-
import com.intellij.debugger.SourcePosition
2019
import com.intellij.debugger.engine.DebugProcessImpl
2120
import com.intellij.debugger.engine.NamedMethodFilter
22-
import com.intellij.debugger.impl.DebuggerUtilsEx
2321
import com.intellij.util.Range
2422
import com.sun.jdi.Location
23+
import org.jetbrains.kotlin.descriptors.*
24+
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
2525
import org.jetbrains.kotlin.idea.util.application.runReadAction
2626
import org.jetbrains.kotlin.load.java.JvmAbi
27-
import org.jetbrains.kotlin.psi.KtAnonymousInitializer
28-
import org.jetbrains.kotlin.psi.KtConstructor
29-
import org.jetbrains.kotlin.psi.KtElement
30-
import org.jetbrains.kotlin.psi.KtPropertyAccessor
27+
import org.jetbrains.kotlin.psi.KtClass
28+
import org.jetbrains.kotlin.psi.KtDeclaration
29+
import org.jetbrains.kotlin.psi.KtProperty
30+
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate
31+
import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors
3132

3233
class KotlinBasicStepMethodFilter(
33-
val resolvedElement: KtElement,
34+
val descriptor: CallableDescriptor,
3435
val myCallingExpressionLines: Range<Int>
3536
) : NamedMethodFilter {
3637
private val myTargetMethodName: String
3738

3839
init {
39-
myTargetMethodName = when (resolvedElement) {
40-
is KtAnonymousInitializer -> "<init>"
41-
is KtConstructor<*> -> "<init>"
42-
is KtPropertyAccessor -> JvmAbi.getterName((resolvedElement.property).name!!)
43-
else -> resolvedElement.name!!
40+
myTargetMethodName = when (descriptor) {
41+
is ClassDescriptor, is ConstructorDescriptor -> "<init>"
42+
is PropertyAccessorDescriptor -> JvmAbi.getterName(descriptor.correspondingProperty.name.asString())
43+
else -> descriptor.name.asString()
4444
}
4545
}
4646

@@ -52,13 +52,35 @@ class KotlinBasicStepMethodFilter(
5252
val method = location.method()
5353
if (myTargetMethodName != method.name()) return false
5454

55-
val sourcePosition = runReadAction { SourcePosition.createFromElement(resolvedElement) } ?: return false
5655
val positionManager = process.positionManager ?: return false
5756

58-
val classes = positionManager.getAllClasses(sourcePosition)
57+
val descriptor = runReadAction {
58+
val elementAt = positionManager.getSourcePosition(location)?.elementAt
5959

60-
return classes.any {
61-
it == location.declaringType() || DebuggerUtilsEx.isAssignableFrom(it.name(), location.declaringType())
60+
val declaration = elementAt?.getParentOfTypesAndPredicate(false, KtDeclaration::class.java) {
61+
it !is KtProperty || !it.isLocal
62+
}
63+
64+
if (declaration is KtClass && method.name() == "<init>") {
65+
(declaration.resolveToDescriptor() as? ClassDescriptor)?.unsubstitutedPrimaryConstructor
66+
} else {
67+
declaration?.resolveToDescriptor()
68+
}
69+
} ?: return false
70+
71+
fun compareDescriptors(d1: DeclarationDescriptor, d2: DeclarationDescriptor): Boolean {
72+
return d1 == d2 || d1.original == d2.original
73+
}
74+
75+
if (compareDescriptors(descriptor, this.descriptor)) return true
76+
77+
if (descriptor is CallableDescriptor) {
78+
return descriptor.findTopMostOverriddenDescriptors().any { compareDescriptors(it, this.descriptor) } ||
79+
this.descriptor.findTopMostOverriddenDescriptors().any { compareDescriptors(it, descriptor)}
6280
}
81+
82+
return false
6383
}
84+
85+
6486
}

idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinMethodSmartStepIntoTarget.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@ package org.jetbrains.kotlin.idea.debugger.stepping
33
import com.intellij.debugger.actions.SmartStepTarget
44
import com.intellij.psi.PsiElement
55
import com.intellij.util.Range
6+
import org.jetbrains.kotlin.descriptors.CallableDescriptor
67
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
78
import org.jetbrains.kotlin.idea.KotlinIcons
89
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
9-
import org.jetbrains.kotlin.psi.KtElement
10-
import org.jetbrains.kotlin.psi.KtNamedFunction
1110
import org.jetbrains.kotlin.renderer.ParameterNameRenderingPolicy
11+
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
1212
import javax.swing.Icon
1313

1414
class KotlinMethodSmartStepTarget(
15-
val resolvedElement: KtElement,
15+
val descriptor: CallableDescriptor,
1616
label: String,
1717
highlightElement: PsiElement,
1818
lines: Range<Int>
1919
): SmartStepTarget(label, highlightElement, false, lines) {
2020
override fun getIcon(): Icon? {
2121
return when {
22-
resolvedElement is KtNamedFunction && resolvedElement.receiverTypeReference != null -> KotlinIcons.EXTENSION_FUNCTION
22+
descriptor.isExtension -> KotlinIcons.EXTENSION_FUNCTION
2323
else -> KotlinIcons.FUNCTION
2424
}
2525
}
@@ -43,8 +43,8 @@ class KotlinMethodSmartStepTarget(
4343

4444
if (other == null || other !is KotlinMethodSmartStepTarget) return false
4545

46-
return resolvedElement == other.resolvedElement
46+
return descriptor == other.descriptor
4747
}
4848

49-
override fun hashCode() = resolvedElement.hashCode()
49+
override fun hashCode() = descriptor.hashCode()
5050
}

idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSmartStepIntoHandler.kt

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ import com.intellij.util.Range
2727
import com.intellij.util.containers.OrderedSet
2828
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
2929
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
30+
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
3031
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
3132
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
3233
import org.jetbrains.kotlin.idea.caches.resolve.analyze
3334
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
3435
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
3536
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
37+
import org.jetbrains.kotlin.load.java.isFromJava
3638
import org.jetbrains.kotlin.psi.*
3739
import org.jetbrains.kotlin.resolve.BindingContext
3840
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentCall
@@ -146,20 +148,15 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
146148
val delegatedResolvedCall = bindingContext[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, getterDescriptor]
147149
if (delegatedResolvedCall == null) {
148150
val getter = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, getterDescriptor)
149-
if (getter is KtPropertyAccessor && (getter.bodyExpression != null || getter.equalsToken != null)) {
151+
if (getter is KtPropertyAccessor && getter.hasBody()) {
150152
val label = KotlinMethodSmartStepTarget.calcLabel(getterDescriptor)
151-
result.add(KotlinMethodSmartStepTarget(getter, label, expression, lines))
153+
result.add(KotlinMethodSmartStepTarget(getterDescriptor, label, expression, lines))
152154
}
153155
}
154156
else {
155157
val delegatedPropertyGetterDescriptor = delegatedResolvedCall.resultingDescriptor
156-
if (delegatedPropertyGetterDescriptor is CallableMemberDescriptor) {
157-
val function = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, delegatedPropertyGetterDescriptor)
158-
if (function is KtNamedFunction || function is KtSecondaryConstructor) {
159-
val label = "${propertyDescriptor.name}." + KotlinMethodSmartStepTarget.calcLabel(delegatedPropertyGetterDescriptor)
160-
result.add(KotlinMethodSmartStepTarget(function as KtFunction, label, expression, lines))
161-
}
162-
}
158+
val label = "${propertyDescriptor.name}." + KotlinMethodSmartStepTarget.calcLabel(delegatedPropertyGetterDescriptor)
159+
result.add(KotlinMethodSmartStepTarget(delegatedPropertyGetterDescriptor, label, expression, lines))
163160
}
164161
}
165162
}
@@ -172,19 +169,22 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
172169

173170
val descriptor = resolvedCall.resultingDescriptor
174171
if (descriptor is FunctionDescriptor && !isIntrinsic(descriptor)) {
175-
val resolvedElement = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor)
176-
if (resolvedElement is KtNamedFunction || resolvedElement is KtConstructor<*>) {
177-
val label = KotlinMethodSmartStepTarget.calcLabel(descriptor)
178-
result.add(KotlinMethodSmartStepTarget(resolvedElement as KtFunction, label, expression, lines))
179-
}
180-
else if (resolvedElement is PsiMethod) {
181-
result.add(MethodSmartStepTarget(resolvedElement, null, expression, false, lines))
172+
if (descriptor.isFromJava) {
173+
(DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor) as? PsiMethod)?.let {
174+
result.add(MethodSmartStepTarget(it, null, expression, false, lines))
175+
}
182176
}
183-
else if (resolvedElement is KtClass) {
184-
resolvedElement.getAnonymousInitializers().firstOrNull()?.let {
185-
val label = KotlinMethodSmartStepTarget.calcLabel(descriptor)
186-
result.add(KotlinMethodSmartStepTarget(it, label, expression, lines))
187-
}
177+
else {
178+
if (descriptor is ConstructorDescriptor && descriptor.isPrimary) {
179+
val psiElement = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, descriptor)
180+
if (psiElement is KtClass && psiElement.getAnonymousInitializers().isEmpty()) {
181+
// There is no constructor or init block, so do not show it in smart step into
182+
return
183+
}
184+
}
185+
186+
val label = KotlinMethodSmartStepTarget.calcLabel(descriptor)
187+
result.add(KotlinMethodSmartStepTarget(descriptor, label, expression, lines))
188188
}
189189
}
190190
}
@@ -195,7 +195,7 @@ class KotlinSmartStepIntoHandler : JvmSmartStepIntoHandler() {
195195

196196
override fun createMethodFilter(stepTarget: SmartStepTarget?): MethodFilter? {
197197
return when (stepTarget) {
198-
is KotlinMethodSmartStepTarget -> KotlinBasicStepMethodFilter(stepTarget.resolvedElement, stepTarget.callingExpressionLines!!)
198+
is KotlinMethodSmartStepTarget -> KotlinBasicStepMethodFilter(stepTarget.descriptor, stepTarget.callingExpressionLines!!)
199199
is KotlinLambdaSmartStepTarget -> KotlinLambdaMethodFilter(stepTarget.getLambda(), stepTarget.callingExpressionLines!!, stepTarget.isInline)
200200
else -> super.createMethodFilter(stepTarget)
201201
}

idea/testData/debugger/tinyApp/outs/smartStepIntoConstructor.out

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,36 @@ LineBreakpoint created at smartStepIntoConstructor.kt:35
99
LineBreakpoint created at smartStepIntoConstructor.kt:39
1010
LineBreakpoint created at smartStepIntoConstructor.kt:43
1111
LineBreakpoint created at smartStepIntoConstructor.kt:47
12+
LineBreakpoint created at smartStepIntoConstructor.kt:51
13+
LineBreakpoint created at smartStepIntoConstructor.kt:55
1214
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! smartStepIntoConstructor.SmartStepIntoConstructorKt
1315
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
1416
smartStepIntoConstructor.kt:7
15-
smartStepIntoConstructor.kt:51
17+
smartStepIntoConstructor.kt:59
1618
smartStepIntoConstructor.kt:11
17-
smartStepIntoConstructor.kt:52
19+
smartStepIntoConstructor.kt:60
1820
smartStepIntoConstructor.kt:15
19-
smartStepIntoConstructor.kt:54
21+
smartStepIntoConstructor.kt:62
2022
smartStepIntoConstructor.kt:19
21-
smartStepIntoConstructor.kt:57
23+
smartStepIntoConstructor.kt:65
2224
smartStepIntoConstructor.kt:23
23-
smartStepIntoConstructor.kt:61
25+
smartStepIntoConstructor.kt:69
2426
smartStepIntoConstructor.kt:27
25-
smartStepIntoConstructor.kt:66
27+
smartStepIntoConstructor.kt:74
2628
smartStepIntoConstructor.kt:31
27-
smartStepIntoConstructor.kt:69
29+
smartStepIntoConstructor.kt:77
2830
smartStepIntoConstructor.kt:35
29-
smartStepIntoConstructor.kt:74
31+
smartStepIntoConstructor.kt:82
3032
smartStepIntoConstructor.kt:39
31-
smartStepIntoConstructor.kt:81
32-
smartStepIntoConstructor.kt:43
3333
smartStepIntoConstructor.kt:89
34-
smartStepIntoConstructor.kt:47
34+
smartStepIntoConstructor.kt:43
3535
smartStepIntoConstructor.kt:97
36+
smartStepIntoConstructor.kt:47
37+
smartStepIntoConstructor.kt:105
38+
smartStepIntoConstructor.kt:51
39+
smartStepIntoConstructor.kt:112
40+
smartStepIntoConstructor.kt:55
41+
smartStepIntoConstructor.kt:113
3642
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
3743

3844
Process finished with exit code 0
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
LineBreakpoint created at smartStepIntoInterfaceFun.kt:23
2+
LineBreakpoint created at smartStepIntoInterfaceFun.kt:27
3+
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! smartStepIntoInterfaceFun.SmartStepIntoInterfaceFunKt
4+
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
5+
smartStepIntoInterfaceFun.kt:23
6+
smartStepIntoInterfaceFun.kt:11
7+
smartStepIntoInterfaceFun.kt:27
8+
smartStepIntoInterfaceFun.kt:14
9+
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
10+
11+
Process finished with exit code 0
12+
13+

idea/testData/debugger/tinyApp/src/stepping/custom/smartStepIntoConstructor.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ fun main(args: Array<String>) {
4545
// RESUME: 1
4646
//Breakpoint!
4747
N(1)
48+
// SMART_STEP_INTO_BY_INDEX: 1
49+
// RESUME: 1
50+
//Breakpoint!
51+
O(1)
52+
// SMART_STEP_INTO_BY_INDEX: 1
53+
// RESUME: 1
54+
//Breakpoint!
55+
O(1, "1")
4856

4957
}
5058

@@ -100,4 +108,8 @@ class N {
100108

101109
constructor() {
102110
}
111+
}
112+
class O<T>(i: T) {
113+
constructor(i: Int, j: T): this(j) {
114+
}
103115
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// KT-13485
2+
package smartStepIntoInterfaceFun
3+
4+
interface ObjectFace<T> {
5+
fun act()
6+
fun act2(t: T)
7+
}
8+
9+
class ObjectClass : ObjectFace<Int> {
10+
override fun act() {
11+
println()
12+
}
13+
override fun act2(t: Int) {
14+
println()
15+
}
16+
}
17+
18+
fun main(args: Array<String>) {
19+
val simple: ObjectFace<Int> = ObjectClass()
20+
// SMART_STEP_INTO_BY_INDEX: 1
21+
// RESUME: 1
22+
//Breakpoint!
23+
simple.act()
24+
// SMART_STEP_INTO_BY_INDEX: 1
25+
// RESUME: 1
26+
//Breakpoint!
27+
simple.act2(1)
28+
}

idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
5555
import org.jetbrains.kotlin.test.InTextDirectivesUtils
5656
import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes
5757
import java.io.File
58+
import java.lang.AssertionError
5859
import javax.swing.SwingUtilities
5960

6061
abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
@@ -213,7 +214,7 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
213214
stepTarget ->
214215
when (stepTarget) {
215216
is KotlinLambdaSmartStepTarget -> KotlinLambdaMethodFilter(stepTarget.getLambda(), stepTarget.getCallingExpressionLines()!!, stepTarget.isInline)
216-
is KotlinMethodSmartStepTarget -> KotlinBasicStepMethodFilter(stepTarget.resolvedElement, stepTarget.getCallingExpressionLines()!!)
217+
is KotlinMethodSmartStepTarget -> KotlinBasicStepMethodFilter(stepTarget.descriptor, stepTarget.getCallingExpressionLines()!!)
217218
is MethodSmartStepTarget -> BasicStepMethodFilter(stepTarget.method, stepTarget.getCallingExpressionLines())
218219
else -> null
219220
}

0 commit comments

Comments
 (0)