Support "Force Step Over" action over suspended calls (KT-18453)
#KT-18453 Fixed
This commit is contained in:
@@ -921,6 +921,7 @@ fun main(args: Array<String>) {
|
||||
model("debugger/tinyApp/src/stepping/stepInto", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepIntoTest", testClassName = "StepIntoOnly")
|
||||
model("debugger/tinyApp/src/stepping/stepOut", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepOutTest")
|
||||
model("debugger/tinyApp/src/stepping/stepOver", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepOverTest")
|
||||
model("debugger/tinyApp/src/stepping/stepOverForce", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepOverForceTest")
|
||||
model("debugger/tinyApp/src/stepping/filters", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doStepIntoTest")
|
||||
model("debugger/tinyApp/src/stepping/custom", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doCustomTest")
|
||||
}
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() {
|
||||
val location = suspendContext.debugProcess.invokeInManagerThread { suspendContext.frameProxy?.location() } ?: return null
|
||||
if (isInSuspendMethod(location) && !isOnSuspendReturnOrReenter(location) && !isLastLineLocationInMethod(location)) {
|
||||
return DebugProcessImplHelper.createStepOverCommandWithCustomFilter(
|
||||
suspendContext, ignoreBreakpoints, KotlinSuspendCallStepOverFilter(sourcePosition.line, file))
|
||||
suspendContext, ignoreBreakpoints, KotlinSuspendCallStepOverFilter(sourcePosition.line, file, ignoreBreakpoints))
|
||||
}
|
||||
|
||||
return null
|
||||
|
||||
+11
-4
@@ -32,7 +32,10 @@ import org.jetbrains.kotlin.idea.debugger.isOnSuspendReturnOrReenter
|
||||
import org.jetbrains.kotlin.idea.debugger.suspendFunctionFirstLineLocation
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
|
||||
class KotlinSuspendCallStepOverFilter(private val line: Int, private val file: PsiFile) : MethodFilter {
|
||||
class KotlinSuspendCallStepOverFilter(
|
||||
private val line: Int,
|
||||
private val file: PsiFile,
|
||||
private val ignoreBreakpoints: Boolean) : MethodFilter {
|
||||
override fun getCallingExpressionLines(): Range<Int>? = Range(line, line)
|
||||
|
||||
override fun locationMatches(process: DebugProcessImpl, location: Location?): Boolean {
|
||||
@@ -47,20 +50,24 @@ class KotlinSuspendCallStepOverFilter(private val line: Int, private val file: P
|
||||
val breakpointManager = DebuggerManagerEx.getInstanceEx(debugProcess.project).breakpointManager
|
||||
breakpointManager.applyThreadFilter(debugProcess, null)
|
||||
|
||||
createRunToCursorBreakpoint(context, suspendStartLineNumber - 1, file)
|
||||
createRunToCursorBreakpoint(context, suspendStartLineNumber - 1, file, ignoreBreakpoints)
|
||||
return RequestHint.RESUME
|
||||
}
|
||||
}
|
||||
|
||||
private fun createRunToCursorBreakpoint(context: SuspendContextImpl, line: Int, file: PsiFile) {
|
||||
private fun createRunToCursorBreakpoint(context: SuspendContextImpl, line: Int, file: PsiFile, ignoreBreakpoints: Boolean) {
|
||||
val position = XSourcePositionImpl.create(file.virtualFile, line) ?: return
|
||||
val process = context.debugProcess
|
||||
process.showStatusText(DebuggerBundle.message("status.run.to.cursor"))
|
||||
process.cancelRunToCursorBreakpoint()
|
||||
|
||||
if (ignoreBreakpoints) {
|
||||
DebuggerManagerEx.getInstanceEx(process.project).breakpointManager.disableBreakpoints(process)
|
||||
}
|
||||
|
||||
val runToCursorBreakpoint =
|
||||
runReadAction {
|
||||
DebuggerManagerEx.getInstanceEx(process.project).breakpointManager.addRunToCursorBreakpoint(position, false)
|
||||
DebuggerManagerEx.getInstanceEx(process.project).breakpointManager.addRunToCursorBreakpoint(position, ignoreBreakpoints)
|
||||
} ?:
|
||||
return
|
||||
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package sofSuspendableCallInFun
|
||||
|
||||
import forTests.builder
|
||||
import kotlin.coroutines.experimental.Continuation
|
||||
import kotlin.coroutines.experimental.suspendCoroutine
|
||||
|
||||
private fun foo(a: Any) {}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
builder {
|
||||
inFun()
|
||||
}
|
||||
|
||||
foo("Main end")
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
suspend fun inFun() {
|
||||
//Breakpoint!
|
||||
run()
|
||||
foo("End")
|
||||
}
|
||||
|
||||
suspend fun run() {
|
||||
//Breakpoint!
|
||||
foo("This breakpoint should be skipped")
|
||||
|
||||
suspendCoroutine { cont: Continuation<Unit> ->
|
||||
Thread {
|
||||
cont.resume(Unit)
|
||||
Thread.sleep(10)
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
// STEP_OVER_FORCE: 2
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
LineBreakpoint created at sofSuspendableCallInFun.kt:20
|
||||
LineBreakpoint created at sofSuspendableCallInFun.kt:26
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
sofSuspendableCallInFun.kt:20
|
||||
sofSuspendableCallInFun.kt:18
|
||||
sofSuspendableCallInFun.kt:21
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -33,6 +33,10 @@ abstract class AbstractKotlinSteppingTest : KotlinDebuggerTestBase() {
|
||||
doTest(path, "STEP_OVER")
|
||||
}
|
||||
|
||||
protected fun doStepOverForceTest(path: String) {
|
||||
doTest(path, "STEP_OVER_FORCE")
|
||||
}
|
||||
|
||||
protected fun doSmartStepIntoTest(path: String) {
|
||||
doTest(path, "SMART_STEP_INTO")
|
||||
}
|
||||
|
||||
@@ -177,9 +177,10 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
dp.managerThread.schedule(stepOutCommand)
|
||||
}
|
||||
|
||||
protected fun SuspendContextImpl.doStepOver() {
|
||||
val stepOverCommand = runReadAction { commandProvider.getStepOverCommand(this, false, debuggerContext) }
|
||||
?: dp.createStepOverCommand(this, false)
|
||||
protected fun SuspendContextImpl.doStepOver(ignoreBreakpoints: Boolean = false) {
|
||||
val stepOverCommand =
|
||||
runReadAction { commandProvider.getStepOverCommand(this, ignoreBreakpoints, debuggerContext) } ?:
|
||||
dp.createStepOverCommand(this, ignoreBreakpoints)
|
||||
dp.managerThread.schedule(stepOverCommand)
|
||||
}
|
||||
|
||||
@@ -203,6 +204,7 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
line.startsWith("// STEP_INTO: ") -> repeat("// STEP_INTO: ") { doStepInto(false, null) }
|
||||
line.startsWith("// STEP_OUT: ") -> repeat("// STEP_OUT: ") { doStepOut() }
|
||||
line.startsWith("// STEP_OVER: ") -> repeat("// STEP_OVER: ") { doStepOver() }
|
||||
line.startsWith("// STEP_OVER_FORCE: ") -> repeat("// STEP_OVER_FORCE: ") { doStepOver(true) }
|
||||
line.startsWith("// SMART_STEP_INTO_BY_INDEX: ") -> doOnBreakpoint { doSmartStepInto(InTextDirectivesUtils.getPrefixedInt(line, "// SMART_STEP_INTO_BY_INDEX: ")!!) }
|
||||
line.startsWith("// SMART_STEP_INTO: ") -> repeat("// SMART_STEP_INTO: ") { doSmartStepInto() }
|
||||
line.startsWith("// RESUME: ") -> repeat("// RESUME: ") { resume(this) }
|
||||
|
||||
@@ -963,6 +963,21 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOverForce")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class StepOverForce extends AbstractKotlinSteppingTest {
|
||||
public void testAllFilesPresentInStepOverForce() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/stepOverForce"), Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("sofSuspendableCallInFun.kt")
|
||||
public void testSofSuspendableCallInFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOverForce/sofSuspendableCallInFun.kt");
|
||||
doStepOverForceTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/debugger/tinyApp/src/stepping/filters")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user