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>hacky</w>
<w>impls</w>
<w>inlined</w>
<w>kapt</w>
<w>kotlinc</w>
<w>mutators</w>
@@ -59,7 +59,8 @@ public class DebuggerSteppingHelper {
if (frameProxy != null) {
Location location = frameProxy.location();
KotlinStepAction action = KotlinSteppingCommandProviderKt.getStepOverAction(location, sourcePosition, frameProxy);
KotlinStepAction action = KotlinSteppingCommandProviderKt
.getStepOverAction(location, sourcePosition, suspendContext, frameProxy);
createStepRequest(
suspendContext, getContextThread(),
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.idea.debugger.stepping
import com.intellij.debugger.PositionManager
import com.intellij.debugger.SourcePosition
import com.intellij.debugger.engine.DebugProcessImpl
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.JvmSteppingCommandProvider
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.settings.DebuggerSettings
import com.intellij.psi.PsiElement
import com.sun.jdi.AbsentInformationException
import com.sun.jdi.LocalVariable
import com.sun.jdi.Location
import com.sun.jdi.StackFrame
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
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.LocationToken
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.name.FqName
import org.jetbrains.kotlin.psi.*
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.resolve.inline.InlineUtil
import kotlin.math.max
@@ -156,12 +162,16 @@ interface KotlinMethodFilter : MethodFilter {
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 method = location.safeMethod() ?: return KotlinStepAction.StepOver
val token = LocationToken.from(stackFrame).takeIf { it.lineNumber > 0 } ?: return KotlinStepAction.StepOver
val inlinedFunctionArgumentRanges = sourcePosition.collectInlineFunctionArgumentRanges()
val positionManager = suspendContext.debugProcess.positionManager
val tokensToSkip = mutableSetOf(token)
@@ -174,6 +184,7 @@ fun getStepOverAction(location: Location, sourcePosition: SourcePosition, frameP
&& candidateToken.lineNumber != token.lineNumber
&& inlinedFunctionArgumentRanges.none { range -> range.contains(candidateKotlinLineNumber) }
&& candidateToken.inlineVariables.none { it !in token.inlineVariables }
&& !isInlineFunctionFromLibrary(positionManager, candidate, candidateToken)
if (!isAcceptable) {
tokensToSkip += candidateToken
@@ -183,6 +194,36 @@ fun getStepOverAction(location: Location, sourcePosition: SourcePosition, frameP
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> {
return runReadAction {
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");
}
@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")
public void testNoParameterExtensionLambdaArgumentCallInInline() throws Exception {
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