Debugger: Skip inlined library frames on step over (KT-12016)

This commit is contained in:
Yan Zhulanow
2020-02-04 23:15:17 +09:00
parent 7490c229ac
commit dd33640238
12 changed files with 166 additions and 2 deletions
+1
View File
@@ -6,6 +6,7 @@
<w>destructured</w> <w>destructured</w>
<w>hacky</w> <w>hacky</w>
<w>impls</w> <w>impls</w>
<w>inlined</w>
<w>kapt</w> <w>kapt</w>
<w>kotlinc</w> <w>kotlinc</w>
<w>mutators</w> <w>mutators</w>
@@ -59,7 +59,8 @@ public class DebuggerSteppingHelper {
if (frameProxy != null) { if (frameProxy != null) {
Location location = frameProxy.location(); Location location = frameProxy.location();
KotlinStepAction action = KotlinSteppingCommandProviderKt.getStepOverAction(location, sourcePosition, frameProxy); KotlinStepAction action = KotlinSteppingCommandProviderKt
.getStepOverAction(location, sourcePosition, suspendContext, frameProxy);
createStepRequest( createStepRequest(
suspendContext, getContextThread(), suspendContext, getContextThread(),
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.idea.debugger.stepping package org.jetbrains.kotlin.idea.debugger.stepping
import com.intellij.debugger.PositionManager
import com.intellij.debugger.SourcePosition import com.intellij.debugger.SourcePosition
import com.intellij.debugger.engine.DebugProcessImpl import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.MethodFilter import com.intellij.debugger.engine.MethodFilter
@@ -12,12 +13,14 @@ import com.intellij.debugger.engine.SuspendContextImpl
import com.intellij.debugger.impl.DebuggerContextImpl import com.intellij.debugger.impl.DebuggerContextImpl
import com.intellij.debugger.impl.JvmSteppingCommandProvider import com.intellij.debugger.impl.JvmSteppingCommandProvider
import com.intellij.debugger.jdi.StackFrameProxyImpl import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.settings.DebuggerSettings
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.sun.jdi.AbsentInformationException import com.sun.jdi.AbsentInformationException
import com.sun.jdi.LocalVariable import com.sun.jdi.LocalVariable
import com.sun.jdi.Location import com.sun.jdi.Location
import com.sun.jdi.StackFrame import com.sun.jdi.StackFrame
import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
import org.jetbrains.kotlin.idea.core.util.getLineNumber import org.jetbrains.kotlin.idea.core.util.getLineNumber
@@ -25,9 +28,12 @@ import org.jetbrains.kotlin.idea.debugger.*
import org.jetbrains.kotlin.idea.debugger.stepping.filter.KotlinSuspendCallStepOverFilter import org.jetbrains.kotlin.idea.debugger.stepping.filter.KotlinSuspendCallStepOverFilter
import org.jetbrains.kotlin.idea.debugger.stepping.filter.LocationToken import org.jetbrains.kotlin.idea.debugger.stepping.filter.LocationToken
import org.jetbrains.kotlin.idea.debugger.stepping.filter.StepOverCallerInfo import org.jetbrains.kotlin.idea.debugger.stepping.filter.StepOverCallerInfo
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.inline.InlineUtil
import kotlin.math.max import kotlin.math.max
@@ -156,12 +162,16 @@ interface KotlinMethodFilter : MethodFilter {
fun locationMatches(context: SuspendContextImpl, location: Location): Boolean fun locationMatches(context: SuspendContextImpl, location: Location): Boolean
} }
fun getStepOverAction(location: Location, sourcePosition: SourcePosition, frameProxy: StackFrameProxyImpl): KotlinStepAction { fun getStepOverAction(
location: Location, sourcePosition: SourcePosition,
suspendContext: SuspendContextImpl, frameProxy: StackFrameProxyImpl
): KotlinStepAction {
val stackFrame = frameProxy.safeStackFrame() ?: return KotlinStepAction.StepOver val stackFrame = frameProxy.safeStackFrame() ?: return KotlinStepAction.StepOver
val method = location.safeMethod() ?: return KotlinStepAction.StepOver val method = location.safeMethod() ?: return KotlinStepAction.StepOver
val token = LocationToken.from(stackFrame).takeIf { it.lineNumber > 0 } ?: return KotlinStepAction.StepOver val token = LocationToken.from(stackFrame).takeIf { it.lineNumber > 0 } ?: return KotlinStepAction.StepOver
val inlinedFunctionArgumentRanges = sourcePosition.collectInlineFunctionArgumentRanges() val inlinedFunctionArgumentRanges = sourcePosition.collectInlineFunctionArgumentRanges()
val positionManager = suspendContext.debugProcess.positionManager
val tokensToSkip = mutableSetOf(token) val tokensToSkip = mutableSetOf(token)
@@ -174,6 +184,7 @@ fun getStepOverAction(location: Location, sourcePosition: SourcePosition, frameP
&& candidateToken.lineNumber != token.lineNumber && candidateToken.lineNumber != token.lineNumber
&& inlinedFunctionArgumentRanges.none { range -> range.contains(candidateKotlinLineNumber) } && inlinedFunctionArgumentRanges.none { range -> range.contains(candidateKotlinLineNumber) }
&& candidateToken.inlineVariables.none { it !in token.inlineVariables } && candidateToken.inlineVariables.none { it !in token.inlineVariables }
&& !isInlineFunctionFromLibrary(positionManager, candidate, candidateToken)
if (!isAcceptable) { if (!isAcceptable) {
tokensToSkip += candidateToken tokensToSkip += candidateToken
@@ -183,6 +194,36 @@ fun getStepOverAction(location: Location, sourcePosition: SourcePosition, frameP
return KotlinStepAction.StepOverInlined(tokensToSkip, StepOverCallerInfo.from(location)) return KotlinStepAction.StepOverInlined(tokensToSkip, StepOverCallerInfo.from(location))
} }
private fun isInlineFunctionFromLibrary(positionManager: PositionManager, location: Location, token: LocationToken): Boolean {
if (token.inlineVariables.isEmpty()) {
return false
}
val debuggerSettings = DebuggerSettings.getInstance()
if (!debuggerSettings.TRACING_FILTERS_ENABLED) {
return false
}
tailrec fun getDeclarationName(element: PsiElement?): FqName? {
val declaration = element?.getNonStrictParentOfType<KtDeclaration>() ?: return null
declaration.getKotlinFqName()?.let { return it }
return getDeclarationName(declaration.parent)
}
val fqn = runReadAction {
val element = positionManager.getSourcePosition(location)?.elementAt
getDeclarationName(element)?.takeIf { !it.isRoot }?.asString()
} ?: return false
for (filter in debuggerSettings.steppingFilters) {
if (filter.isEnabled && filter.matches(fqn)) {
return true
}
}
return false
}
private fun SourcePosition.collectInlineFunctionArgumentRanges(): List<IntRange> { private fun SourcePosition.collectInlineFunctionArgumentRanges(): List<IntRange> {
return runReadAction { return runReadAction {
val ranges = mutableListOf<IntRange>() val ranges = mutableListOf<IntRange>()
@@ -467,6 +467,26 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/kt24343.kt"); runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/kt24343.kt");
} }
@TestMetadata("lambdaToInlineFold.kt")
public void testLambdaToInlineFold() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/lambdaToInlineFold.kt");
}
@TestMetadata("lambdaToInlineFoldFiltersDisabled.kt")
public void testLambdaToInlineFoldFiltersDisabled() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/lambdaToInlineFoldFiltersDisabled.kt");
}
@TestMetadata("lambdaToInlineMap.kt")
public void testLambdaToInlineMap() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/lambdaToInlineMap.kt");
}
@TestMetadata("lambdaToInlineMapFiltersDisabled.kt")
public void testLambdaToInlineMapFiltersDisabled() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/lambdaToInlineMapFiltersDisabled.kt");
}
@TestMetadata("noParameterExtensionLambdaArgumentCallInInline.kt") @TestMetadata("noParameterExtensionLambdaArgumentCallInInline.kt")
public void testNoParameterExtensionLambdaArgumentCallInInline() throws Exception { public void testNoParameterExtensionLambdaArgumentCallInInline() throws Exception {
runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/noParameterExtensionLambdaArgumentCallInInline.kt"); runTest("idea/jvm-debugger/jvm-debugger-test/testData/stepping/stepOver/noParameterExtensionLambdaArgumentCallInInline.kt");
@@ -0,0 +1,11 @@
package test
fun main() {
val l = listOf(1, 2, 3, 4)
val res = l.foldRightIndexed(0) { index, elem, akk ->
//Breakpoint!
akk + index - 4
}
}
// STEP_OVER: 5
@@ -0,0 +1,12 @@
LineBreakpoint created at lambdaToInlineFold.kt:7
Run Java
Connected to the target VM
lambdaToInlineFold.kt:7
lambdaToInlineFold.kt:7
lambdaToInlineFold.kt:7
lambdaToInlineFold.kt:7
lambdaToInlineFold.kt:5
lambdaToInlineFold.kt:9
Disconnected from the target VM
Process finished with exit code 0
@@ -0,0 +1,12 @@
package test
fun main() {
val l = listOf(1, 2, 3, 4)
val res = l.foldRightIndexed(0) { index, elem, akk ->
//Breakpoint!
akk + index - 4
}
}
// TRACING_FILTERS_ENABLED: false
// STEP_OVER: 5
@@ -0,0 +1,14 @@
LineBreakpoint created at lambdaToInlineFoldFiltersDisabled.kt:7
Run Java
Connected to the target VM
lambdaToInlineFoldFiltersDisabled.kt:7
_Collections.!EXT!
_Collections.!EXT!
_Collections.!EXT!
lambdaToInlineFoldFiltersDisabled.kt:7
_Collections.!EXT!
resuming lambdaToInlineFoldFiltersDisabled.kt:7
resuming lambdaToInlineFoldFiltersDisabled.kt:7
Disconnected from the target VM
Process finished with exit code 0
@@ -0,0 +1,13 @@
package test
fun main() {
val l = listOf(1, 2, 3, 4)
l.map { element ->
//Breakpoint!
bar(element * 2)
}
}
fun bar(n: Int) = n
// STEP_OVER: 5
@@ -0,0 +1,11 @@
LineBreakpoint created at lambdaToInlineMap.kt:7
Run Java
Connected to the target VM
lambdaToInlineMap.kt:7
lambdaToInlineMap.kt:7
lambdaToInlineMap.kt:7
lambdaToInlineMap.kt:7
lambdaToInlineMap.kt:9
Disconnected from the target VM
Process finished with exit code 0
@@ -0,0 +1,14 @@
package test
fun main() {
val l = listOf(1, 2, 3, 4)
l.map { element ->
//Breakpoint!
bar(element * 2)
}
}
fun bar(n: Int) = n
// TRACING_FILTERS_ENABLED: false
// STEP_OVER: 5
@@ -0,0 +1,14 @@
LineBreakpoint created at lambdaToInlineMapFiltersDisabled.kt:7
Run Java
Connected to the target VM
lambdaToInlineMapFiltersDisabled.kt:7
_Collections.!EXT!
_Collections.!EXT!
lambdaToInlineMapFiltersDisabled.kt:7
_Collections.!EXT!
_Collections.!EXT!
resuming lambdaToInlineMapFiltersDisabled.kt:7
resuming lambdaToInlineMapFiltersDisabled.kt:7
Disconnected from the target VM
Process finished with exit code 0