Better diagnostic for inlined line under dex on step over

This commit is contained in:
Nikolay Krasko
2016-11-15 22:01:55 +03:00
parent 2817e1a5f1
commit 6f95667f54
8 changed files with 58 additions and 27 deletions
@@ -122,16 +122,8 @@ class KotlinPositionManager(private val myDebugProcess: DebugProcess) : MultiReq
}
if (sourceLineNumber > psiFile.getLineCount() && myDebugProcess.isDexDebug()) {
val thisFunLine = getLastLineNumberForLocation(location, myDebugProcess.project)
if (thisFunLine != null && thisFunLine != location.lineNumber()) {
return SourcePosition.createFromLine(psiFile, thisFunLine - 1)
}
val inlinePosition = getOriginalPositionOfInlinedLine(location, myDebugProcess.project)
if (inlinePosition != null) {
return SourcePosition.createFromLine(inlinePosition.first, inlinePosition.second)
}
val (line, ktFile) = ktLocationInfo(location, true, myDebugProcess.project, false, psiFile)
return SourcePosition.createFromLine(ktFile ?: psiFile, line - 1)
}
return SourcePosition.createFromLine(psiFile, sourceLineNumber)
@@ -67,26 +67,30 @@ fun readBytecodeInfo(project: Project,
return KotlinDebuggerCaches.getOrReadDebugInfoFromBytecode(project, jvmName, file)
}
fun noStrataLineNumber(location: Location, isDexDebug: Boolean, project: Project, preferInlined: Boolean = false): Int {
if (isDexDebug) {
fun ktLocationInfo(location: Location, isDexDebug: Boolean, project: Project,
preferInlined: Boolean = false, locationFile: KtFile? = null): Pair<Int, KtFile?> {
if (isDexDebug && (locationFile == null || location.lineNumber() > locationFile.getLineCount())) {
if (!preferInlined) {
val thisFunLine = runReadAction { getLastLineNumberForLocation(location, project) }
if (thisFunLine != null && thisFunLine != location.lineNumber()) {
// TODO: bad line because of inlining
return thisFunLine
return thisFunLine to locationFile
}
}
val inlinePosition = runReadAction { getOriginalPositionOfInlinedLine(location, project) }
if (inlinePosition != null) {
return inlinePosition.second + 1
val (file, line) = inlinePosition
return line + 1 to file
}
}
return location.lineNumber()
return location.lineNumber() to locationFile
}
/**
* Only the first line number is stored for instruction in dex. It can be obtained through location.lineNumber().
* This method allows to get last stored linenumber for instruction.
*/
fun getLastLineNumberForLocation(location: Location, project: Project, searchScope: GlobalSearchScope = GlobalSearchScope.allScope(project)): Int? {
val lineNumber = location.lineNumber()
val fqName = FqName(location.declaringType().name())
@@ -22,7 +22,7 @@ import com.intellij.openapi.project.Project
import com.intellij.util.Range
import com.sun.jdi.LocalVariable
import com.sun.jdi.Location
import org.jetbrains.kotlin.idea.debugger.noStrataLineNumber
import org.jetbrains.kotlin.idea.debugger.ktLocationInfo
class StepOverFilterData(
val lineNumber: Int,
@@ -33,7 +33,7 @@ class StepOverFilterData(
)
class KotlinStepOverInlineFilter(val project: Project, val data: StepOverFilterData) : KotlinMethodFilter {
private fun Location.ktLineNumber() = noStrataLineNumber(this, data.isDexDebug, project)
private fun Location.ktLineNumber() = ktLocationInfo(this, data.isDexDebug, project).first
override fun locationMatches(context: SuspendContextImpl, location: Location): Boolean {
val frameProxy = context.frameProxy ?: return true
@@ -41,7 +41,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
import org.jetbrains.kotlin.idea.debugger.DebuggerUtils
import org.jetbrains.kotlin.idea.debugger.noStrataLineNumber
import org.jetbrains.kotlin.idea.debugger.ktLocationInfo
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.GOTO
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.MOVE
import org.jetbrains.kotlin.idea.debugger.stepping.DexBytecode.RETURN
@@ -317,7 +317,7 @@ fun getStepOverAction(
fun getStepOverAction(
location: Location,
file: KtFile,
sourceFile: KtFile,
range: IntRange,
inlineFunctionArguments: List<KtElement>,
frameProxy: StackFrameProxyImpl,
@@ -325,12 +325,17 @@ fun getStepOverAction(
): Action {
location.declaringType() ?: return Action.STEP_OVER()
val project = file.project
val project = sourceFile.project
val methodLocations = location.method().allLineLocations()
val ktLineNumbers = methodLocations.keysToMap { noStrataLineNumber(it, isDexDebug, project, true) }
val locationsLineAndFile = methodLocations.keysToMap { ktLocationInfo(it, isDexDebug, project, true) }
fun Location.ktLineNumber(): Int = ktLineNumbers[this] ?: noStrataLineNumber(this, isDexDebug, project, true)
fun Location.ktLineNumber(): Int = (locationsLineAndFile[this] ?: ktLocationInfo(this, isDexDebug, project, true)).first
fun Location.ktFileName(): String {
val ktFile = (locationsLineAndFile[this] ?: ktLocationInfo(this, isDexDebug, project, true)).second
// File is not null only for inlined locations. Get file name from debugger information otherwise.
return ktFile?.name ?: this.sourceName(KOTLIN_STRATA_NAME)
}
fun isLocationSuitable(nextLocation: Location): Boolean {
if (nextLocation.method() != location.method()) {
@@ -342,8 +347,8 @@ fun getStepOverAction(
return false
}
return try {
nextLocation.sourceName(KOTLIN_STRATA_NAME) == file.name
try {
return nextLocation.ktFileName() == sourceFile.name
}
catch(e: AbsentInformationException) {
return true
@@ -362,7 +367,7 @@ fun getStepOverAction(
}
val patchedLocation = if (isBackEdgeLocation()) {
// Pretend we had already did a backing step
// Pretend we had already done a backing step
methodLocations
.filter(::isLocationSuitable)
.firstOrNull { it.ktLineNumber() == location.ktLineNumber() } ?: location
@@ -0,0 +1,8 @@
LineBreakpoint created at stopInInlineInOtherFileWithLambdaArgumentDex.Other.kt:6
!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! stopInInlineInOtherFileWithLambdaArgumentDex.StopInInlineInOtherFileWithLambdaArgumentDexKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
stopInInlineInOtherFileWithLambdaArgumentDex.Other.kt:6
stopInInlineInOtherFileWithLambdaArgumentDex.Other.kt:7
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
Process finished with exit code 0
@@ -0,0 +1,8 @@
package stopInInlineInOtherFileWithLambdaArgumentDex
inline fun inlineFun(a: () -> Unit) {
a()
// Breakpoint 1
a()
a()
}
@@ -0,0 +1,8 @@
package stopInInlineInOtherFileWithLambdaArgumentDex
fun main(args: Array<String>) {
inlineFun { "hi" }
val i = 1
}
// ADDITIONAL_BREAKPOINT: stopInInlineInOtherFileWithLambdaArgumentDex.Other.kt: Breakpoint 1
@@ -715,6 +715,12 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlineInOtherFileDex.kt");
doStepOverTest(fileName);
}
@TestMetadata("stopInInlineInOtherFileWithLambdaArgumentDex.kt")
public void testStopInInlineInOtherFileWithLambdaArgumentDex() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlineInOtherFileWithLambdaArgumentDex.kt");
doStepOverTest(fileName);
}
}
@TestMetadata("idea/testData/debugger/tinyApp/src/stepping/filters")