diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt index 38e3d972bd5..d5926712cd3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinPositionManager.kt @@ -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) diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/NoStrataPositionManagerHelper.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/NoStrataPositionManagerHelper.kt index ee0d9e87eb5..0301e4c1689 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/NoStrataPositionManagerHelper.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/NoStrataPositionManagerHelper.kt @@ -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 { + 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()) diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepOverInlineFilter.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepOverInlineFilter.kt index 6ecc858ef7d..0cc12590a9a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepOverInlineFilter.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinStepOverInlineFilter.kt @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt index 5c561fa410b..87b6e61e420 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt @@ -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, 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 diff --git a/idea/testData/debugger/tinyApp/outs/stopInInlineInOtherFileWithLambdaArgumentDex.out b/idea/testData/debugger/tinyApp/outs/stopInInlineInOtherFileWithLambdaArgumentDex.out new file mode 100644 index 00000000000..c86b40e2ee7 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/stopInInlineInOtherFileWithLambdaArgumentDex.out @@ -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 diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlineInOtherFileWithLambdaArgumentDex.Other.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlineInOtherFileWithLambdaArgumentDex.Other.kt new file mode 100644 index 00000000000..4e03fc5302e --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlineInOtherFileWithLambdaArgumentDex.Other.kt @@ -0,0 +1,8 @@ +package stopInInlineInOtherFileWithLambdaArgumentDex + +inline fun inlineFun(a: () -> Unit) { + a() + // Breakpoint 1 + a() + a() +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlineInOtherFileWithLambdaArgumentDex.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlineInOtherFileWithLambdaArgumentDex.kt new file mode 100644 index 00000000000..578fdb2ba20 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/stopInInlineInOtherFileWithLambdaArgumentDex.kt @@ -0,0 +1,8 @@ +package stopInInlineInOtherFileWithLambdaArgumentDex + +fun main(args: Array) { + inlineFun { "hi" } + val i = 1 +} + +// ADDITIONAL_BREAKPOINT: stopInInlineInOtherFileWithLambdaArgumentDex.Other.kt: Breakpoint 1 \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java index e04a7270b95..76c979e4067 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java @@ -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")