From 850568b8e91e19730eb6da248cd943161626495f Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Tue, 13 Jun 2017 18:16:18 +0300 Subject: [PATCH] Support "Force Step Over" action over suspended calls (KT-18453) #KT-18453 Fixed --- .../kotlin/generators/tests/GenerateTests.kt | 1 + .../stepping/KotlinSteppingCommandProvider.kt | 2 +- .../KotlinSuspendCallStepOverFilter.kt | 15 +++++--- .../stepOverForce/sofSuspendableCallInFun.kt | 36 +++++++++++++++++++ .../stepOverForce/sofSuspendableCallInFun.out | 10 ++++++ .../debugger/AbstractKotlinSteppingTest.kt | 4 +++ .../idea/debugger/KotlinDebuggerTestBase.kt | 8 +++-- .../debugger/KotlinSteppingTestGenerated.java | 15 ++++++++ 8 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOverForce/sofSuspendableCallInFun.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOverForce/sofSuspendableCallInFun.out diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 0d324224a13..4a5bc0cd621 100755 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -921,6 +921,7 @@ fun main(args: Array) { 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") } 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 86490625be1..d404d28f3af 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSuspendCallStepOverFilter.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSuspendCallStepOverFilter.kt index f0b7ad75fe8..e69573702dd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSuspendCallStepOverFilter.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSuspendCallStepOverFilter.kt @@ -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? = 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 diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOverForce/sofSuspendableCallInFun.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOverForce/sofSuspendableCallInFun.kt new file mode 100644 index 00000000000..55b5c6ba312 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOverForce/sofSuspendableCallInFun.kt @@ -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) { + 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 -> + Thread { + cont.resume(Unit) + Thread.sleep(10) + }.start() + } +} + +// STEP_OVER_FORCE: 2 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOverForce/sofSuspendableCallInFun.out b/idea/testData/debugger/tinyApp/src/stepping/stepOverForce/sofSuspendableCallInFun.out new file mode 100644 index 00000000000..0c79ed31d31 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOverForce/sofSuspendableCallInFun.out @@ -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 diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractKotlinSteppingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractKotlinSteppingTest.kt index 0d8470bc8d5..9c8d5e2939d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractKotlinSteppingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractKotlinSteppingTest.kt @@ -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") } diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt index 42864d98a80..aee9152bf8e 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt @@ -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) } diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java index bd944a267c4..df412d19d8b 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java @@ -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)