From 0e8e8ef54613630b690f63a9f2d4d06a761a84de Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Mon, 29 May 2017 19:20:15 +0300 Subject: [PATCH] Step over for suspended calls (KT-18453) Debugger do the normal step over action and checks if function is going to suspend. In this case the "run-to-cursor" breakpoint is installed on function enter to intercept re-enter into function after suspension. #KT-18453 In Progress --- .../kotlin/idea/KotlinExtraSteppingFilter.kt | 6 ++ .../kotlin/idea/debugger/debuggerUtil.kt | 62 ++++++++++++- .../stepping/DebugProcessImplHelper.java | 45 +++++++++ .../stepping/KotlinSteppingCommandProvider.kt | 25 +++-- .../KotlinSuspendCallStepOverFilter.kt | 74 +++++++++++++++ .../stepping/RequestHintWithMethodFilter.kt | 91 +++++++++++++++++++ .../tinyApp/src/stepping/custom/coroutine.kt | 6 +- .../tinyApp/src/stepping/custom/coroutine.out | 1 + .../stepInto/stepIntoSuspendFunctionSimple.kt | 21 +++++ .../stepIntoSuspendFunctionSimple.out | 8 ++ .../stepOver/soNonSuspendableSuspendCall.kt | 21 +++++ .../stepOver/soNonSuspendableSuspendCall.out | 8 ++ .../stepOver/soSuspendableCallInEndOfFun.kt | 33 +++++++ .../stepOver/soSuspendableCallInEndOfFun.out | 11 +++ .../soSuspendableCallInEndOfLambda.kt | 29 ++++++ .../soSuspendableCallInEndOfLambda.out | 9 ++ .../stepOver/soSuspendableCallInFun.kt | 33 +++++++ .../stepOver/soSuspendableCallInFun.out | 9 ++ ...soSuspendableCallInFunFromOtherStepping.kt | 34 +++++++ ...oSuspendableCallInFunFromOtherStepping.out | 10 ++ .../stepOver/soSuspendableCallInLambda.kt | 29 ++++++ .../stepOver/soSuspendableCallInLambda.out | 9 ++ .../debugger/KotlinSteppingTestGenerated.java | 42 +++++++++ 23 files changed, 603 insertions(+), 13 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebugProcessImplHelper.java create mode 100644 idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSuspendCallStepOverFilter.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/debugger/stepping/RequestHintWithMethodFilter.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepInto/stepIntoSuspendFunctionSimple.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepInto/stepIntoSuspendFunctionSimple.out create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soNonSuspendableSuspendCall.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soNonSuspendableSuspendCall.out create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfFun.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfFun.out create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfLambda.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfLambda.out create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFun.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFun.out create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFunFromOtherStepping.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFunFromOtherStepping.out create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInLambda.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInLambda.out diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinExtraSteppingFilter.kt b/idea/src/org/jetbrains/kotlin/idea/KotlinExtraSteppingFilter.kt index d0dc21fc893..f1a90c60a27 100644 --- a/idea/src/org/jetbrains/kotlin/idea/KotlinExtraSteppingFilter.kt +++ b/idea/src/org/jetbrains/kotlin/idea/KotlinExtraSteppingFilter.kt @@ -23,6 +23,8 @@ import com.intellij.debugger.settings.DebuggerSettings import com.sun.jdi.Location import com.sun.jdi.request.StepRequest import org.jetbrains.kotlin.idea.debugger.KotlinPositionManager +import org.jetbrains.kotlin.idea.debugger.isOnSuspendReturnOrReenter +import org.jetbrains.kotlin.idea.debugger.isOneLineMethod import org.jetbrains.kotlin.idea.util.application.runReadAction class KotlinExtraSteppingFilter : ExtraSteppingFilter { @@ -54,6 +56,10 @@ class KotlinExtraSteppingFilter : ExtraSteppingFilter { return false } ?: return false + if (isOnSuspendReturnOrReenter(location) && !isOneLineMethod(location)) { + return true + } + val settings = DebuggerSettings.getInstance() if (settings.TRACING_FILTERS_ENABLED) { val classNames = positionManager.originalClassNameForPosition(sourcePosition).map { it.replace('/', '.') } diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt index 825fa836d00..f5383c3f42b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt @@ -17,12 +17,15 @@ package org.jetbrains.kotlin.idea.debugger import com.intellij.debugger.engine.DebugProcessImpl +import com.intellij.debugger.engine.DebuggerManagerThreadImpl import com.intellij.debugger.engine.events.DebuggerCommandImpl import com.intellij.debugger.impl.DebuggerContextImpl import com.intellij.debugger.jdi.LocalVariableProxyImpl import com.sun.jdi.* import com.sun.tools.jdi.LocalVariableImpl import org.jetbrains.kotlin.codegen.binding.CodegenBinding +import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_ASM_TYPE +import org.jetbrains.kotlin.codegen.coroutines.DO_RESUME_METHOD_NAME import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.load.java.JvmAbi @@ -54,13 +57,21 @@ fun isInsideInlineArgument(inlineArgument: KtFunction, location: Location, debug } != null } -fun DebugProcessImpl.invokeInManagerThread(f: (DebuggerContextImpl) -> T?): T? { +fun DebugProcessImpl.invokeInManagerThread(f: (DebuggerContextImpl) -> T?): T? { var result: T? = null - managerThread.invokeAndWait(object : DebuggerCommandImpl() { + val command: DebuggerCommandImpl = object : DebuggerCommandImpl() { override fun action() { result = runReadAction { f(debuggerContext) } } - }) + } + + when { + DebuggerManagerThreadImpl.isManagerThread() -> + managerThread.invoke(command) + else -> + managerThread.invokeAndWait(command) + } + return result } @@ -137,3 +148,48 @@ private class MockStackFrame(private val location: Location, private val vm: Vir override fun getArgumentValues(): List = emptyList() override fun virtualMachine() = vm } + +private val DO_RESUME_SIGNATURE = "(Ljava/lang/Object;Ljava/lang/Throwable;)Ljava/lang/Object;" + +fun isInSuspendMethod(location: Location): Boolean { + val method = location.method() + val signature = method.signature() + + return signature.contains(CONTINUATION_ASM_TYPE.toString()) || + (method.name() == DO_RESUME_METHOD_NAME && signature == DO_RESUME_SIGNATURE) +} + +fun suspendFunctionFirstLineLocation(location: Location): Int? { + if (!isInSuspendMethod(location)) { + return null + } + + val lineNumber = location.method().location().lineNumber() + if (lineNumber == -1) { + return null + } + + return lineNumber +} + +fun isOnSuspendReturnOrReenter(location: Location): Boolean { + val suspendStartLineNumber = suspendFunctionFirstLineLocation(location) ?: return false + return suspendStartLineNumber == location.lineNumber() +} + +fun isLastLineLocationInMethod(location: Location): Boolean { + val knownLines = location.method().allLineLocations().map { it.lineNumber() }.filter { it != -1 } + if (knownLines.isEmpty()) { + return false + } + + return knownLines.max() == location.lineNumber() +} + +fun isOneLineMethod(location: Location): Boolean { + val allLineLocations = location.method().allLineLocations() + val firstLine = allLineLocations.firstOrNull()?.lineNumber() + val lastLine = allLineLocations.lastOrNull()?.lineNumber() + + return firstLine != null && firstLine == lastLine +} diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebugProcessImplHelper.java b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebugProcessImplHelper.java new file mode 100644 index 00000000000..7fbc7347422 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebugProcessImplHelper.java @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.debugger.stepping; + +import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.RequestHint; +import com.intellij.debugger.engine.SuspendContextImpl; +import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; +import com.sun.jdi.request.StepRequest; +import org.jetbrains.annotations.NotNull; + +public class DebugProcessImplHelper { + public static DebugProcessImpl.StepOverCommand createStepOverCommandWithCustomFilter( + SuspendContextImpl suspendContext, + boolean ignoreBreakpoints, + KotlinSuspendCallStepOverFilter methodFilter) { + DebugProcessImpl debugProcess = suspendContext.getDebugProcess(); + return debugProcess.new StepOverCommand(suspendContext, ignoreBreakpoints, StepRequest.STEP_LINE) { + @NotNull + @Override + protected RequestHint getHint(SuspendContextImpl suspendContext, ThreadReferenceProxyImpl stepThread) { + @SuppressWarnings("MagicConstant") + RequestHint hint = new RequestHintWithMethodFilter(stepThread, suspendContext, StepRequest.STEP_OVER, methodFilter); + hint.setRestoreBreakpoints(ignoreBreakpoints); + hint.setIgnoreFilters(ignoreBreakpoints || debugProcess.getSession().shouldIgnoreSteppingFilters()); + + return hint; + } + }; + } +} 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 6e456e0fd23..86490625be1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSteppingCommandProvider.kt @@ -38,8 +38,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze 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.ktLocationInfo +import org.jetbrains.kotlin.idea.debugger.* 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 @@ -89,9 +88,18 @@ class KotlinSteppingCommandProvider : JvmSteppingCommandProvider() { sourcePosition: SourcePosition): DebugProcessImpl.ResumeCommand? { val kotlinSourcePosition = KotlinSourcePosition.create(sourcePosition) ?: return null - if (!isSpecialStepOverNeeded(kotlinSourcePosition)) return null + if (isSpecialStepOverNeeded(kotlinSourcePosition)) { + return DebuggerSteppingHelper.createStepOverCommand(suspendContext, ignoreBreakpoints, kotlinSourcePosition) + } - return DebuggerSteppingHelper.createStepOverCommand(suspendContext, ignoreBreakpoints, kotlinSourcePosition) + val file = sourcePosition.elementAt.containingFile + 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)) + } + + return null } data class KotlinSourcePosition(val file: KtFile, val function: KtNamedFunction, @@ -204,9 +212,8 @@ private fun getInlineArgumentsCallsIfAny(sourcePosition: SourcePosition, declara fun isCallOfArgument(ktCallExpression: KtCallExpression): Boolean { val context = ktCallExpression.analyze(BodyResolveMode.PARTIAL) - val resolvedCall = ktCallExpression.getResolvedCall(context) ?: return false + val resolvedCall = ktCallExpression.getResolvedCall(context) as? VariableAsFunctionResolvedCallImpl ?: return false - if (resolvedCall !is VariableAsFunctionResolvedCallImpl) return false val candidateDescriptor = resolvedCall.variableCall.candidateDescriptor return candidateDescriptor in valueParameters @@ -394,8 +401,8 @@ fun getStepOverAction( .dropWhile { it != patchedLocation } .drop(1) .dropWhile { it.ktLineNumber() == patchedLineNumber } - .takeWhile { location -> - !isLocationSuitable(location) || lambdaArgumentRanges.any { location.ktLineNumber() in it } + .takeWhile { loc -> + !isLocationSuitable(loc) || lambdaArgumentRanges.any { loc.ktLineNumber() in it } } .dropWhile { it.ktLineNumber() == patchedLineNumber } @@ -579,4 +586,4 @@ object DexBytecode { val GOTO = 0x28 val MOVE = 0x01 -} \ No newline at end of file +} diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSuspendCallStepOverFilter.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSuspendCallStepOverFilter.kt new file mode 100644 index 00000000000..f0b7ad75fe8 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/KotlinSuspendCallStepOverFilter.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.debugger.stepping + +import com.intellij.debugger.DebuggerBundle +import com.intellij.debugger.DebuggerManagerEx +import com.intellij.debugger.engine.DebugProcessImpl +import com.intellij.debugger.engine.MethodFilter +import com.intellij.debugger.engine.RequestHint +import com.intellij.debugger.engine.SuspendContextImpl +import com.intellij.debugger.settings.DebuggerSettings +import com.intellij.psi.PsiFile +import com.intellij.util.Range +import com.intellij.xdebugger.impl.XSourcePositionImpl +import com.sun.jdi.Location +import com.sun.jdi.request.EventRequest +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 { + override fun getCallingExpressionLines(): Range? = Range(line, line) + + override fun locationMatches(process: DebugProcessImpl, location: Location?): Boolean { + return location != null && isOnSuspendReturnOrReenter(location) + } + + override fun onReached(context: SuspendContextImpl, hint: RequestHint): Int { + val location = context.frameProxy?.location() ?: return RequestHint.STOP + val suspendStartLineNumber = suspendFunctionFirstLineLocation(location) ?: return RequestHint.STOP + + val debugProcess = context.debugProcess + val breakpointManager = DebuggerManagerEx.getInstanceEx(debugProcess.project).breakpointManager + breakpointManager.applyThreadFilter(debugProcess, null) + + createRunToCursorBreakpoint(context, suspendStartLineNumber - 1, file) + return RequestHint.RESUME + } +} + +private fun createRunToCursorBreakpoint(context: SuspendContextImpl, line: Int, file: PsiFile) { + val position = XSourcePositionImpl.create(file.virtualFile, line) ?: return + val process = context.debugProcess + process.showStatusText(DebuggerBundle.message("status.run.to.cursor")) + process.cancelRunToCursorBreakpoint() + + val runToCursorBreakpoint = + runReadAction { + DebuggerManagerEx.getInstanceEx(process.project).breakpointManager.addRunToCursorBreakpoint(position, false) + } ?: + return + + runToCursorBreakpoint.suspendPolicy = when { + context.suspendPolicy == EventRequest.SUSPEND_EVENT_THREAD -> DebuggerSettings.SUSPEND_THREAD + else -> DebuggerSettings.SUSPEND_ALL + } + + runToCursorBreakpoint.createRequest(process) + process.setRunToCursorBreakpoint(runToCursorBreakpoint) +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/RequestHintWithMethodFilter.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/RequestHintWithMethodFilter.kt new file mode 100644 index 00000000000..34fe9ead0f5 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/RequestHintWithMethodFilter.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.debugger.stepping + +import com.intellij.debugger.engine.BreakpointStepMethodFilter +import com.intellij.debugger.engine.MethodFilter +import com.intellij.debugger.engine.RequestHint +import com.intellij.debugger.engine.SuspendContextImpl +import com.intellij.debugger.engine.evaluation.EvaluateException +import com.intellij.debugger.jdi.ThreadReferenceProxyImpl +import com.intellij.openapi.diagnostic.Logger +import com.sun.jdi.VMDisconnectedException +import com.sun.jdi.request.StepRequest +import org.intellij.lang.annotations.MagicConstant +import java.lang.reflect.Field + +internal class RequestHintWithMethodFilter( + stepThread: ThreadReferenceProxyImpl, + suspendContext: SuspendContextImpl, + @MagicConstant(intValues = longArrayOf( + StepRequest.STEP_INTO.toLong(), + StepRequest.STEP_OVER.toLong(), + StepRequest.STEP_OUT.toLong())) depth: Int, + methodFilter: MethodFilter +) : RequestHint(stepThread, suspendContext, methodFilter) { + private var targetMethodMatched = false + + init { + // NOTE: Debugger API. Open RequestHint constructor with depth + if (depth != StepRequest.STEP_INTO) { + findFieldWithValue(StepRequest.STEP_INTO, Integer.TYPE)?.setInt(this, depth) + } + } + + private fun findFieldWithValue(value: Int, type: Class<*>): Field? { + return RequestHint::class.java.declaredFields.firstOrNull { field -> + if (field.type == type) { + field.isAccessible = true + if (field.getInt(this) == value) { + return@firstOrNull true + } + } + + false + } + } + + override fun getNextStepDepth(context: SuspendContextImpl): Int { + try { + val frameProxy = context.frameProxy + val filter = methodFilter + + if (filter != null && frameProxy != null && filter !is BreakpointStepMethodFilter) { + /*NODE: Debugger API. Base implementation works only for smart step into, and calls filter only if !isTheSameFrame(context). */ + if (filter.locationMatches(context.debugProcess, frameProxy.location())) { + targetMethodMatched = true + return filter.onReached(context, this) + } + } + } + catch (ignored: VMDisconnectedException) { + return STOP + } + catch (e: EvaluateException) { + LOG.error(e) + return STOP + } + + return super.getNextStepDepth(context) + } + + override fun wasStepTargetMethodMatched(): Boolean { + return super.wasStepTargetMethodMatched() || targetMethodMatched + } +} + +private val LOG = Logger.getInstance(RequestHintWithMethodFilter::class.java) diff --git a/idea/testData/debugger/tinyApp/src/stepping/custom/coroutine.kt b/idea/testData/debugger/tinyApp/src/stepping/custom/coroutine.kt index cd882e11038..abf856fd226 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/custom/coroutine.kt +++ b/idea/testData/debugger/tinyApp/src/stepping/custom/coroutine.kt @@ -15,4 +15,8 @@ fun main(args: Array) { builder { first() } -} \ No newline at end of file +} + +// STEP_INTO: 1 + +// TODO: Breakpoint on builder {} is now triggered twice. This is because generated line number on suspend function enter. \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/custom/coroutine.out b/idea/testData/debugger/tinyApp/src/stepping/custom/coroutine.out index 200a3ba8ac3..6842f7ca747 100644 --- a/idea/testData/debugger/tinyApp/src/stepping/custom/coroutine.out +++ b/idea/testData/debugger/tinyApp/src/stepping/custom/coroutine.out @@ -2,6 +2,7 @@ LineBreakpoint created at coroutine.kt:15 Run Java Connected to the target VM coroutine.kt:15 +coroutine.kt:15 coroutine.kt:16 Disconnected from the target VM diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepInto/stepIntoSuspendFunctionSimple.kt b/idea/testData/debugger/tinyApp/src/stepping/stepInto/stepIntoSuspendFunctionSimple.kt new file mode 100644 index 00000000000..2b0886c010f --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepInto/stepIntoSuspendFunctionSimple.kt @@ -0,0 +1,21 @@ +package stepIntoSuspendFunctionSimple + +import forTests.builder + +private fun foo() {} + +suspend fun second() { +} + +suspend fun first(): Int { + second() + return 12 +} + +fun main(args: Array) { + builder { + //Breakpoint! + first() + foo() + } +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepInto/stepIntoSuspendFunctionSimple.out b/idea/testData/debugger/tinyApp/src/stepping/stepInto/stepIntoSuspendFunctionSimple.out new file mode 100644 index 00000000000..0c3e6012dcb --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepInto/stepIntoSuspendFunctionSimple.out @@ -0,0 +1,8 @@ +LineBreakpoint created at stepIntoSuspendFunctionSimple.kt:18 +Run Java +Connected to the target VM +stepIntoSuspendFunctionSimple.kt:18 +stepIntoSuspendFunctionSimple.kt:10 +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soNonSuspendableSuspendCall.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soNonSuspendableSuspendCall.kt new file mode 100644 index 00000000000..79833042782 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soNonSuspendableSuspendCall.kt @@ -0,0 +1,21 @@ +package soNonSuspendableSuspendCall + +import forTests.builder + +private fun foo() {} + +suspend fun second() { +} + +suspend fun first(): Int { + second() + return 12 +} + +fun main(args: Array) { + builder { + //Breakpoint! + first() + foo() + } +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soNonSuspendableSuspendCall.out b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soNonSuspendableSuspendCall.out new file mode 100644 index 00000000000..bf026af3abf --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soNonSuspendableSuspendCall.out @@ -0,0 +1,8 @@ +LineBreakpoint created at soNonSuspendableSuspendCall.kt:18 +Run Java +Connected to the target VM +soNonSuspendableSuspendCall.kt:18 +soNonSuspendableSuspendCall.kt:19 +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfFun.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfFun.kt new file mode 100644 index 00000000000..0c823205adc --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfFun.kt @@ -0,0 +1,33 @@ +package soSuspendableCallInEndOfFun + +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() { + foo("Start") + //Breakpoint! + run() +} + +suspend fun run() { + suspendCoroutine { cont: Continuation -> + Thread { + cont.resume(Unit) + Thread.sleep(10) + }.start() + } +} + +// STEP_OVER: 4 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfFun.out b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfFun.out new file mode 100644 index 00000000000..f4fb0909853 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfFun.out @@ -0,0 +1,11 @@ +LineBreakpoint created at soSuspendableCallInEndOfFun.kt:21 +Run Java +Connected to the target VM +soSuspendableCallInEndOfFun.kt:21 +soSuspendableCallInEndOfFun.kt:18 +soSuspendableCallInEndOfFun.kt:22 +soSuspendableCallInEndOfFun.kt:-1 +soSuspendableCallInEndOfFun.kt:28 +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfLambda.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfLambda.kt new file mode 100644 index 00000000000..b88951c61e8 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfLambda.kt @@ -0,0 +1,29 @@ +package soSuspendableCallInEndOfLambda + +import forTests.builder +import kotlin.coroutines.experimental.Continuation +import kotlin.coroutines.experimental.suspendCoroutine + +private fun foo(a: Any) {} + +fun main(args: Array) { + builder { + foo("Start") + //Breakpoint! + run() + } + + foo("Main end") + Thread.sleep(100) +} + +suspend fun run() { + suspendCoroutine { cont: Continuation -> + Thread { + cont.resume(Unit) + Thread.sleep(10) + }.start() + } +} + +// STEP_OVER: 2 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfLambda.out b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfLambda.out new file mode 100644 index 00000000000..21ee7834760 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfLambda.out @@ -0,0 +1,9 @@ +LineBreakpoint created at soSuspendableCallInEndOfLambda.kt:13 +Run Java +Connected to the target VM +soSuspendableCallInEndOfLambda.kt:13 +soSuspendableCallInEndOfLambda.kt:10 +soSuspendableCallInEndOfLambda.kt:14 +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFun.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFun.kt new file mode 100644 index 00000000000..bf01ada3fa9 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFun.kt @@ -0,0 +1,33 @@ +package soSuspendableCallInFun + +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() { + suspendCoroutine { cont: Continuation -> + Thread { + cont.resume(Unit) + Thread.sleep(10) + }.start() + } +} + +// STEP_OVER: 2 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFun.out b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFun.out new file mode 100644 index 00000000000..172f32012cb --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFun.out @@ -0,0 +1,9 @@ +LineBreakpoint created at soSuspendableCallInFun.kt:20 +Run Java +Connected to the target VM +soSuspendableCallInFun.kt:20 +soSuspendableCallInFun.kt:18 +soSuspendableCallInFun.kt:21 +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFunFromOtherStepping.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFunFromOtherStepping.kt new file mode 100644 index 00000000000..934027ebc36 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFunFromOtherStepping.kt @@ -0,0 +1,34 @@ +package soSuspendableCallInFunFromOtherStepping + +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(120) +} + +suspend fun inFun() { + //Breakpoint! + foo("Start") + run() + foo("End") +} + +suspend fun run() { + return suspendCoroutine { cont: Continuation -> + Thread { + cont.resume(Unit) + Thread.sleep(10) + }.start() + } +} + +// STEP_OVER: 3 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFunFromOtherStepping.out b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFunFromOtherStepping.out new file mode 100644 index 00000000000..7e24da9f664 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFunFromOtherStepping.out @@ -0,0 +1,10 @@ +LineBreakpoint created at soSuspendableCallInFunFromOtherStepping.kt:20 +Run Java +Connected to the target VM +soSuspendableCallInFunFromOtherStepping.kt:20 +soSuspendableCallInFunFromOtherStepping.kt:21 +soSuspendableCallInFunFromOtherStepping.kt:18 +soSuspendableCallInFunFromOtherStepping.kt:22 +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInLambda.kt b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInLambda.kt new file mode 100644 index 00000000000..b61a6798594 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInLambda.kt @@ -0,0 +1,29 @@ +package soSuspendableCallInLambda + +import forTests.builder +import kotlin.coroutines.experimental.Continuation +import kotlin.coroutines.experimental.suspendCoroutine + +private fun foo(a: Any) {} + +fun main(args: Array) { + builder { + //Breakpoint! + run() + foo("End") + } + + foo("Main end") + Thread.sleep(100) +} + +suspend fun run() { + suspendCoroutine { cont: Continuation -> + Thread { + cont.resume(Unit) + Thread.sleep(10) + }.start() + } +} + +// STEP_OVER: 2 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInLambda.out b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInLambda.out new file mode 100644 index 00000000000..7021513dbf6 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInLambda.out @@ -0,0 +1,9 @@ +LineBreakpoint created at soSuspendableCallInLambda.kt:12 +Run Java +Connected to the target VM +soSuspendableCallInLambda.kt:12 +soSuspendableCallInLambda.kt:10 +soSuspendableCallInLambda.kt:13 +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java index dba69fa7975..bd944a267c4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java @@ -314,6 +314,12 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doStepIntoTest(fileName); } + @TestMetadata("stepIntoSuspendFunctionSimple.kt") + public void testStepIntoSuspendFunctionSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepInto/stepIntoSuspendFunctionSimple.kt"); + doStepIntoTest(fileName); + } + @TestMetadata("syntheticMethods.kt") public void testSyntheticMethods() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepInto/syntheticMethods.kt"); @@ -608,6 +614,12 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doStepOverTest(fileName); } + @TestMetadata("soNonSuspendableSuspendCall.kt") + public void testSoNonSuspendableSuspendCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soNonSuspendableSuspendCall.kt"); + doStepOverTest(fileName); + } + @TestMetadata("soReifiedInlineIfConditionFalse.kt") public void testSoReifiedInlineIfConditionFalse() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soReifiedInlineIfConditionFalse.kt"); @@ -620,6 +632,36 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doStepOverTest(fileName); } + @TestMetadata("soSuspendableCallInEndOfFun.kt") + public void testSoSuspendableCallInEndOfFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfFun.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soSuspendableCallInEndOfLambda.kt") + public void testSoSuspendableCallInEndOfLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInEndOfLambda.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soSuspendableCallInFun.kt") + public void testSoSuspendableCallInFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFun.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soSuspendableCallInFunFromOtherStepping.kt") + public void testSoSuspendableCallInFunFromOtherStepping() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInFunFromOtherStepping.kt"); + doStepOverTest(fileName); + } + + @TestMetadata("soSuspendableCallInLambda.kt") + public void testSoSuspendableCallInLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/soSuspendableCallInLambda.kt"); + doStepOverTest(fileName); + } + @TestMetadata("stepOverCatchClause.kt") public void testStepOverCatchClause() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverCatchClause.kt");