[coroutines-debugger]: Fix stepping through and leaving scope coroutines

* Fixed extraction of continuation for `ScopeCoroutine` and moved the computation to the Helper class
* When stepping over the closing bracket of the suspend block -> step out


Merge-request: IJ-MR-136650
Merged-by: Maria Sokolova <maria.sokolova@jetbrains.com>

GitOrigin-RevId: c391c0097530c37d0c2d10fae88df221fffa0fd9
This commit is contained in:
Maria Sokolova
2024-06-14 16:47:27 +00:00
committed by intellij-monorepo-bot
parent 9ad79a14f0
commit 039028fe06
34 changed files with 1141 additions and 844 deletions

View File

@@ -56,6 +56,25 @@ public final class CoroutinesDebugHelper {
return getCoroutineOwner(continuation, false);
}
public static Object getCallerFrame(Object continuation) throws ReflectiveOperationException {
// This method extracts the caller frame of the given continuation.
Class<?> coroutineStackFrame = Class.forName("kotlin.coroutines.jvm.internal.CoroutineStackFrame", false, continuation.getClass().getClassLoader());
Method getCallerFrame = coroutineStackFrame.getDeclaredMethod("getCallerFrame");
getCallerFrame.setAccessible(true);
Object callerFrame = getCallerFrame.invoke(continuation);
// In case the caller frame is the root CoroutineOwner completion added by the debug agent -> return the current continuation
if (callerFrame == null || callerFrame.getClass().getSimpleName().contains(COROUTINE_OWNER_CLASS)) {
return continuation;
}
// In case the caller frame is an instance of ScopeCoroutine, then extract the uCont that is wrapped by the ScopeCoroutine class.
// ScopeCoroutine is used to wrap the current continuation and pass it into withContext/coroutineScope/flow.. invocation
Class<?> scopeCoroutine = Class.forName("kotlinx.coroutines.internal.ScopeCoroutine", false, continuation.getClass().getClassLoader());
if (scopeCoroutine.isInstance(callerFrame)) {
return getCallerFrame.invoke(callerFrame);
}
return callerFrame;
}
private static Object getField(Object object, String fieldName) throws ReflectiveOperationException {
Field field = object.getClass().getField(fieldName);
field.setAccessible(true);