Skip to content

Commit a265283

Browse files
committed
Step over locations after return in dex debug
1 parent f07b5ea commit a265283

File tree

4 files changed

+47
-34
lines changed

4 files changed

+47
-34
lines changed

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ fun isInlineFunctionLineNumber(file: VirtualFile, lineNumber: Int, project: Proj
6161
return true
6262
}
6363

64-
fun readDebugBytecodeInfo(project: Project,
65-
jvmName: JvmClassName,
66-
file: VirtualFile): BytecodeDebugInfo? {
64+
fun readBytecodeInfo(project: Project,
65+
jvmName: JvmClassName,
66+
file: VirtualFile): BytecodeDebugInfo? {
6767
return KotlinDebuggerCaches.getOrReadDebugInfoFromBytecode(project, jvmName, file)
6868
}
6969

@@ -240,7 +240,7 @@ private fun findAndReadClassFile(
240240
val virtualFile = file.virtualFile ?: return null
241241
if (!fileFilter(virtualFile)) return null
242242

243-
return readDebugBytecodeInfo(project, jvmClassName, virtualFile)
243+
return readBytecodeInfo(project, jvmClassName, virtualFile)
244244
}
245245

246246
internal fun getLocationsOfInlinedLine(type: ReferenceType, position: SourcePosition, sourceSearchScope: GlobalSearchScope): List<Location> {
@@ -272,7 +272,7 @@ private fun inlinedLinesNumbers(
272272

273273
val virtualFile = file.virtualFile ?: return listOf()
274274

275-
val debugInfo = readDebugBytecodeInfo(project, jvmClassName, virtualFile) ?: return listOf()
275+
val debugInfo = readBytecodeInfo(project, jvmClassName, virtualFile) ?: return listOf()
276276
val smapData = debugInfo.smapData ?: return listOf()
277277

278278
val smap = smapData.kotlinStrata ?: return listOf()

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,34 @@ import com.sun.jdi.LocalVariable
2424
import com.sun.jdi.Location
2525
import org.jetbrains.kotlin.idea.debugger.noStrataLineNumber
2626

27-
class KotlinStepOverInlineFilter(
27+
class StepOverFilterData(
28+
val lineNumber: Int,
29+
val stepOverLines: Set<Int>,
30+
val inlineRangeVariables: List<LocalVariable>,
2831
val isDexDebug: Boolean,
29-
val project: Project,
30-
val stepOverLines: Set<Int>, val fromLine: Int,
31-
val inlineFunRangeVariables: List<LocalVariable>) : KotlinMethodFilter {
32-
private fun Location.ktLineNumber() = noStrataLineNumber(this, isDexDebug, project)
32+
val skipAfterCodeIndex: Long = -1
33+
)
34+
35+
class KotlinStepOverInlineFilter(val project: Project, val data: StepOverFilterData) : KotlinMethodFilter {
36+
private fun Location.ktLineNumber() = noStrataLineNumber(this, data.isDexDebug, project)
3337

3438
override fun locationMatches(context: SuspendContextImpl, location: Location): Boolean {
3539
val frameProxy = context.frameProxy ?: return true
3640

41+
if (data.skipAfterCodeIndex != -1L && location.codeIndex() > data.skipAfterCodeIndex) {
42+
return false
43+
}
44+
3745
val currentLine = location.ktLineNumber()
38-
if (!(stepOverLines.contains(currentLine))) {
39-
return currentLine != fromLine
46+
if (!(data.stepOverLines.contains(currentLine))) {
47+
return currentLine != data.lineNumber
4048
}
4149

4250
val visibleInlineVariables = getInlineRangeLocalVariables(frameProxy)
4351

4452
// Our ranges check missed exit from inline function. This is when breakpoint was in last statement of inline functions.
4553
// This can be observed by inline local range-variables. Absence of any means step out was done.
46-
return inlineFunRangeVariables.any { !visibleInlineVariables.contains(it) }
54+
return data.inlineRangeVariables.any { !visibleInlineVariables.contains(it) }
4755
}
4856

4957
override fun locationMatches(process: DebugProcessImpl, location: Location): Boolean {

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

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -265,17 +265,12 @@ private fun findCallsOnPosition(sourcePosition: SourcePosition, filter: (KtCallE
265265
}
266266
}
267267

268-
269268
sealed class Action(val position: XSourcePositionImpl? = null,
270-
val lineNumber: Int? = null,
271-
val stepOverLines: Set<Int>? = null,
272-
val inlineRangeVariables: List<LocalVariable>? = null,
273-
val isDexDebug: Boolean = false) {
269+
val stepOverInlineData: StepOverFilterData? = null) {
274270
class STEP_OVER : Action()
275271
class STEP_OUT : Action()
276272
class RUN_TO_CURSOR(position: XSourcePositionImpl) : Action(position)
277-
class STEP_OVER_INLINED(lineNumber: Int, stepOverLines: Set<Int>, inlineVariables: List<LocalVariable>, isDexDebug: Boolean) : Action(
278-
lineNumber = lineNumber, stepOverLines = stepOverLines, inlineRangeVariables = inlineVariables, isDexDebug = isDexDebug)
273+
class STEP_OVER_INLINED(stepOverInlineData: StepOverFilterData) : Action(stepOverInlineData = stepOverInlineData)
279274

280275
fun apply(debugProcess: DebugProcessImpl,
281276
suspendContext: SuspendContextImpl,
@@ -289,12 +284,7 @@ sealed class Action(val position: XSourcePositionImpl? = null,
289284
is Action.STEP_OUT -> debugProcess.createStepOutCommand(suspendContext).contextAction(suspendContext)
290285
is Action.STEP_OVER -> debugProcess.createStepOverCommand(suspendContext, ignoreBreakpoints).contextAction(suspendContext)
291286
is Action.STEP_OVER_INLINED -> KotlinStepActionFactory(debugProcess).createKotlinStepOverInlineAction(
292-
KotlinStepOverInlineFilter(
293-
isDexDebug,
294-
debugProcess.project,
295-
stepOverLines!!,
296-
lineNumber ?: -1,
297-
inlineRangeVariables!!)).contextAction(suspendContext)
287+
KotlinStepOverInlineFilter(debugProcess.project, stepOverInlineData!!)).contextAction(suspendContext)
298288
}
299289
}
300290
}
@@ -336,7 +326,7 @@ fun getStepOverAction(
336326
return false
337327
}
338328

339-
if (nextLocation.ktLineNumber() !in range) {
329+
if (nextLocation.lineNumber() !in range) {
340330
return false
341331
}
342332

@@ -348,8 +338,10 @@ fun getStepOverAction(
348338
}
349339
}
350340

341+
val methodLocations = location.method().allLineLocations()
342+
351343
fun isBackEdgeLocation(): Boolean {
352-
val previousSuitableLocation = computedReferenceType.allLineLocations().reversed()
344+
val previousSuitableLocation = methodLocations.reversed()
353345
.dropWhile { it != location }
354346
.drop(1)
355347
.filter(::isLocationSuitable)
@@ -361,7 +353,7 @@ fun getStepOverAction(
361353

362354
val patchedLocation = if (isBackEdgeLocation()) {
363355
// Pretend we had already did a backing step
364-
computedReferenceType.allLineLocations()
356+
methodLocations
365357
.filter(::isLocationSuitable)
366358
.first { it.ktLineNumber() == location.ktLineNumber() }
367359
}
@@ -388,7 +380,7 @@ fun getStepOverAction(
388380
//
389381
// It also thinks that too many lines are inlined when there's a call of function argument or other
390382
// inline function in last statement of inline function. The list of inlineRangeVariables is used to overcome it.
391-
val probablyInlinedLocations = computedReferenceType.allLineLocations()
383+
val probablyInlinedLocations = methodLocations
392384
.dropWhile { it != patchedLocation }
393385
.drop(1)
394386
.dropWhile { it.ktLineNumber() == patchedLineNumber }
@@ -398,12 +390,25 @@ fun getStepOverAction(
398390
.dropWhile { it.ktLineNumber() == patchedLineNumber }
399391

400392
if (!probablyInlinedLocations.isEmpty()) {
401-
return Action.STEP_OVER_INLINED(
393+
// Some Kotlin inlined methods with 'for' (and maybe others) generates bytecode that after dexing have a strange artifact.
394+
// GOTO instructions are moved to the end of method and as they don't have proper line, line is obtained from the previous
395+
// instruction. It might be method return or previous GOTO from the inlining. Simple stepping over such function is really
396+
// terrible. On each iteration position jumps to the method end or some previous inline call and then returns back. To prevent
397+
// this filter locations with too big code indexes manually
398+
val returnCodeIndex: Long = if (isDexDebug) {
399+
// TODO: won't work for situation when breakpoint is in inlined method
400+
val locationsOfLine = location.method().locationsOfLine(range.last)
401+
locationsOfLine.map { it.codeIndex() }.max() ?: -1L
402+
}
403+
else -1L
404+
405+
return Action.STEP_OVER_INLINED(StepOverFilterData(
402406
patchedLineNumber,
403407
probablyInlinedLocations.map { it.ktLineNumber() }.toSet(),
404408
inlineRangeVariables,
405-
isDexDebug
406-
)
409+
isDexDebug,
410+
returnCodeIndex
411+
))
407412
}
408413

409414
return Action.STEP_OVER()

idea/src/org/jetbrains/kotlin/idea/filters/KotlinExceptionFilter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class KotlinExceptionFilter(private val searchScope: GlobalSearchScope) : Filter
7575
private fun createHyperlinks(jvmName: JvmClassName, file: VirtualFile, line: Int, project: Project): InlineFunctionHyperLinkInfo? {
7676
if (!isInlineFunctionLineNumber(file, line, project)) return null
7777

78-
val debugInfo = readDebugBytecodeInfo(project, jvmName, file) ?: return null
78+
val debugInfo = readBytecodeInfo(project, jvmName, file) ?: return null
7979
val smapData = debugInfo.smapData ?: return null
8080

8181
val inlineInfos = arrayListOf<InlineFunctionHyperLinkInfo.InlineInfo>()

0 commit comments

Comments
 (0)