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
This commit is contained in:
@@ -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('/', '.') }
|
||||
|
||||
@@ -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 <T: Any> DebugProcessImpl.invokeInManagerThread(f: (DebuggerContextImpl) -> T?): T? {
|
||||
fun <T : Any> 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<Value> = 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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+16
-9
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+74
@@ -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<Int>? = 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)
|
||||
}
|
||||
@@ -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)
|
||||
@@ -15,4 +15,8 @@ fun main(args: Array<String>) {
|
||||
builder {
|
||||
first()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// STEP_INTO: 1
|
||||
|
||||
// TODO: Breakpoint on builder {} is now triggered twice. This is because generated line number on suspend function enter.
|
||||
@@ -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
|
||||
|
||||
|
||||
+21
@@ -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<String>) {
|
||||
builder {
|
||||
//Breakpoint!
|
||||
first()
|
||||
foo()
|
||||
}
|
||||
}
|
||||
+8
@@ -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
|
||||
+21
@@ -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<String>) {
|
||||
builder {
|
||||
//Breakpoint!
|
||||
first()
|
||||
foo()
|
||||
}
|
||||
}
|
||||
+8
@@ -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
|
||||
+33
@@ -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<String>) {
|
||||
builder {
|
||||
inFun()
|
||||
}
|
||||
|
||||
foo("Main end")
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
suspend fun inFun() {
|
||||
foo("Start")
|
||||
//Breakpoint!
|
||||
run()
|
||||
}
|
||||
|
||||
suspend fun run() {
|
||||
suspendCoroutine { cont: Continuation<Unit> ->
|
||||
Thread {
|
||||
cont.resume(Unit)
|
||||
Thread.sleep(10)
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
// STEP_OVER: 4
|
||||
+11
@@ -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
|
||||
+29
@@ -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<String>) {
|
||||
builder {
|
||||
foo("Start")
|
||||
//Breakpoint!
|
||||
run()
|
||||
}
|
||||
|
||||
foo("Main end")
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
suspend fun run() {
|
||||
suspendCoroutine { cont: Continuation<Unit> ->
|
||||
Thread {
|
||||
cont.resume(Unit)
|
||||
Thread.sleep(10)
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
// STEP_OVER: 2
|
||||
+9
@@ -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
|
||||
+33
@@ -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<String>) {
|
||||
builder {
|
||||
inFun()
|
||||
}
|
||||
|
||||
foo("Main end")
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
suspend fun inFun() {
|
||||
//Breakpoint!
|
||||
run()
|
||||
foo("End")
|
||||
}
|
||||
|
||||
suspend fun run() {
|
||||
suspendCoroutine { cont: Continuation<Unit> ->
|
||||
Thread {
|
||||
cont.resume(Unit)
|
||||
Thread.sleep(10)
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
// STEP_OVER: 2
|
||||
+9
@@ -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
|
||||
Vendored
+34
@@ -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<String>) {
|
||||
builder {
|
||||
inFun()
|
||||
}
|
||||
|
||||
foo("Main end")
|
||||
Thread.sleep(120)
|
||||
}
|
||||
|
||||
suspend fun inFun() {
|
||||
//Breakpoint!
|
||||
foo("Start")
|
||||
run()
|
||||
foo("End")
|
||||
}
|
||||
|
||||
suspend fun run() {
|
||||
return suspendCoroutine { cont: Continuation<Unit> ->
|
||||
Thread {
|
||||
cont.resume(Unit)
|
||||
Thread.sleep(10)
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
// STEP_OVER: 3
|
||||
Vendored
+10
@@ -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
|
||||
+29
@@ -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<String>) {
|
||||
builder {
|
||||
//Breakpoint!
|
||||
run()
|
||||
foo("End")
|
||||
}
|
||||
|
||||
foo("Main end")
|
||||
Thread.sleep(100)
|
||||
}
|
||||
|
||||
suspend fun run() {
|
||||
suspendCoroutine { cont: Continuation<Unit> ->
|
||||
Thread {
|
||||
cont.resume(Unit)
|
||||
Thread.sleep(10)
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
// STEP_OVER: 2
|
||||
+9
@@ -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
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user