Skip to content

Commit 0032818

Browse files
committed
Step over locations after return in dex debug when no return position found by locations
1 parent a265283 commit 0032818

File tree

1 file changed

+69
-4
lines changed

1 file changed

+69
-4
lines changed

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

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import com.intellij.xdebugger.impl.XSourcePositionImpl
2929
import com.sun.jdi.AbsentInformationException
3030
import com.sun.jdi.LocalVariable
3131
import com.sun.jdi.Location
32+
import com.sun.jdi.Method
3233
import org.jetbrains.annotations.TestOnly
3334
import org.jetbrains.kotlin.builtins.isFunctionType
3435
import org.jetbrains.kotlin.codegen.inline.KOTLIN_STRATA_NAME
@@ -41,6 +42,12 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
4142
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
4243
import org.jetbrains.kotlin.idea.debugger.DebuggerUtils
4344
import org.jetbrains.kotlin.idea.debugger.noStrataLineNumber
45+
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.GOTO
46+
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.MOVE
47+
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.RETURN
48+
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.RETURN_VOID
49+
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.RETURN_OBJECT
50+
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.RETURN_WIDE
4451
import org.jetbrains.kotlin.idea.refactoring.getLineEndOffset
4552
import org.jetbrains.kotlin.idea.refactoring.getLineNumber
4653
import org.jetbrains.kotlin.idea.refactoring.getLineStartOffset
@@ -326,7 +333,7 @@ fun getStepOverAction(
326333
return false
327334
}
328335

329-
if (nextLocation.lineNumber() !in range) {
336+
if (nextLocation.ktLineNumber() !in range) {
330337
return false
331338
}
332339

@@ -396,9 +403,14 @@ fun getStepOverAction(
396403
// terrible. On each iteration position jumps to the method end or some previous inline call and then returns back. To prevent
397404
// this filter locations with too big code indexes manually
398405
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
406+
val method = location.method()
407+
val locationsOfLine = method.locationsOfLine(range.last)
408+
if (locationsOfLine.isNotEmpty()) {
409+
locationsOfLine.map { it.codeIndex() }.max() ?: -1L
410+
}
411+
else {
412+
findReturnFromDexBytecode(location.method())
413+
}
402414
}
403415
else -1L
404416

@@ -512,3 +524,56 @@ private fun getInlineArgumentIfAny(elementAt: PsiElement?): KtFunctionLiteral? {
512524

513525
return functionLiteralExpression.functionLiteral
514526
}
527+
528+
private fun findReturnFromDexBytecode(method: Method): Long {
529+
val methodLocations = method.allLineLocations()
530+
if (methodLocations.isEmpty()) return -1L
531+
532+
var lastMethodCodeIndex = methodLocations.last().codeIndex()
533+
// Continue while it's possible to get location
534+
while (true) {
535+
if (method.locationOfCodeIndex(lastMethodCodeIndex + 1) != null) {
536+
lastMethodCodeIndex++
537+
}
538+
else {
539+
break
540+
}
541+
}
542+
543+
var returnIndex = lastMethodCodeIndex + 1
544+
545+
val bytecode = method.bytecodes()
546+
var i = bytecode.size
547+
548+
while (i >= 2) {
549+
// Can step only through two-byte instructions and abort on any unknown one
550+
i -= 2
551+
returnIndex -= 1
552+
553+
val instruction = bytecode[i].toInt()
554+
555+
if (instruction == RETURN_VOID || instruction == RETURN || instruction == RETURN_WIDE || instruction == RETURN_OBJECT) {
556+
// Instruction found
557+
return returnIndex
558+
}
559+
else if (instruction == MOVE || instruction == GOTO) {
560+
// proceed
561+
}
562+
else {
563+
// Don't know the instruction and it's length. Abort.
564+
break
565+
}
566+
}
567+
568+
return -1L
569+
}
570+
571+
object DexBytecode {
572+
val RETURN_VOID = 0x0e
573+
val RETURN = 0x0f
574+
val RETURN_WIDE = 0x10
575+
val RETURN_OBJECT = 0x11
576+
577+
val GOTO = 0x28
578+
val MOVE = 0x01
579+
}

0 commit comments

Comments
 (0)