coroutine debugger toolbar added
This commit is contained in:
committed by
Vladimir Ilmov
parent
e570450c59
commit
cac4a991f5
+2
-2
@@ -19,12 +19,12 @@ class CoroutineAsyncStackTraceProvider : AsyncStackTraceProvider {
|
|||||||
|
|
||||||
override fun getAsyncStackTrace(stackFrame: JavaStackFrame, suspendContext: SuspendContextImpl): List<CoroutineAsyncStackFrameItem>? {
|
override fun getAsyncStackTrace(stackFrame: JavaStackFrame, suspendContext: SuspendContextImpl): List<CoroutineAsyncStackFrameItem>? {
|
||||||
val stackFrameList = hopelessAware { getAsyncStackTraceSafe(stackFrame.stackFrameProxy, suspendContext) } ?: emptyList()
|
val stackFrameList = hopelessAware { getAsyncStackTraceSafe(stackFrame.stackFrameProxy, suspendContext) } ?: emptyList()
|
||||||
return null
|
return stackFrameList
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAsyncStackTrace(stackFrame: JavaStackFrame, suspendContext: XSuspendContext): List<CoroutineAsyncStackFrameItem>? {
|
fun getAsyncStackTrace(stackFrame: JavaStackFrame, suspendContext: XSuspendContext): List<CoroutineAsyncStackFrameItem>? {
|
||||||
val stackFrameList = hopelessAware { getAsyncStackTraceSafe(stackFrame.stackFrameProxy, suspendContext) } ?: emptyList()
|
val stackFrameList = hopelessAware { getAsyncStackTraceSafe(stackFrame.stackFrameProxy, suspendContext) } ?: emptyList()
|
||||||
return null
|
return stackFrameList
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAsyncStackTraceSafe(frameProxy: StackFrameProxyImpl, suspendContext: XSuspendContext): List<CoroutineAsyncStackFrameItem> {
|
fun getAsyncStackTraceSafe(frameProxy: StackFrameProxyImpl, suspendContext: XSuspendContext): List<CoroutineAsyncStackFrameItem> {
|
||||||
|
|||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.debugger.coroutine
|
||||||
|
|
||||||
|
import com.intellij.ui.components.JBLabel
|
||||||
|
import com.intellij.ui.SimpleListCellRenderer
|
||||||
|
import javax.swing.ListCellRenderer
|
||||||
|
|
||||||
|
class VersionedImplementationProvider() {
|
||||||
|
fun comboboxListCellRenderer(): ListCellRenderer<in String>? =
|
||||||
|
SimpleListCellRenderer.create<String> { label: JBLabel, value: String?, index: Int ->
|
||||||
|
if (value != null) {
|
||||||
|
label.text = value
|
||||||
|
} else if (index >= 0) {
|
||||||
|
label.text = "Loading..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.debugger.coroutine
|
||||||
|
|
||||||
|
import com.intellij.ui.components.JBLabel
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutine.override.com.intellij.ui.SimpleListCellRenderer
|
||||||
|
import javax.swing.ListCellRenderer
|
||||||
|
|
||||||
|
class VersionedImplementationProvider() {
|
||||||
|
fun comboboxListCellRenderer(): ListCellRenderer<in String>? =
|
||||||
|
SimpleListCellRenderer.create<String> { label: JBLabel, value: String?, index: Int ->
|
||||||
|
if (value != null) {
|
||||||
|
label.text = value
|
||||||
|
} else if (index >= 0) {
|
||||||
|
label.text = "Loading..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-7
@@ -71,14 +71,15 @@ class CoroutineBuilder(val suspendContext: XSuspendContext) {
|
|||||||
coroutineStackFrameList.add(RunningCoroutineStackFrameItem(runningStackFrameProxy, xStackFrame))
|
coroutineStackFrameList.add(RunningCoroutineStackFrameItem(runningStackFrameProxy, xStackFrame))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (coroutine.state == CoroutineInfoData.State.SUSPENDED || coroutine.activeThread == null) {
|
} else if ((coroutine.state == CoroutineInfoData.State.SUSPENDED || coroutine.activeThread == null) && coroutine.lastObservedFrameFieldRef is ObjectReference) {
|
||||||
// to get frames from CoroutineInfo anyway
|
// to get frames from CoroutineInfo anyway
|
||||||
// the thread is paused on breakpoint - it has at least one frame
|
// the thread is paused on breakpoint - it has at least one frame
|
||||||
val suspendedStackTrace = coroutine.stackTrace.take(creationFrameSeparatorIndex + 1)
|
val suspendedStackTrace = coroutine.stackTrace.take(creationFrameSeparatorIndex + 1)
|
||||||
for (suspendedFrame in suspendedStackTrace) {
|
for (suspendedFrame in suspendedStackTrace) {
|
||||||
val suspendedXStackFrame = stackFrame(positionManager, firstSuspendedStackFrameProxyImpl, suspendedFrame)
|
val suspendedXStackFrame = stackFrame(positionManager, firstSuspendedStackFrameProxyImpl, suspendedFrame)
|
||||||
|
|
||||||
coroutineStackFrameList.add(
|
coroutineStackFrameList.add(
|
||||||
SuspendCoroutineStackFrameItem(firstSuspendedStackFrameProxyImpl, suspendedFrame, suspendedXStackFrame)
|
SuspendCoroutineStackFrameItem(firstSuspendedStackFrameProxyImpl, suspendedFrame, suspendedXStackFrame, coroutine.lastObservedFrameFieldRef)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,10 +168,6 @@ class CoroutineBuilder(val suspendContext: XSuspendContext) {
|
|||||||
private fun isResumeMethodFrame(frame: StackFrameProxyImpl) =
|
private fun isResumeMethodFrame(frame: StackFrameProxyImpl) =
|
||||||
frame.safeLocation()?.safeMethod()?.name() == "resumeWith"
|
frame.safeLocation()?.safeMethod()?.name() == "resumeWith"
|
||||||
|
|
||||||
/**
|
|
||||||
* Should be invoked on manager thread
|
|
||||||
* This code was migrated from previous implementation, has to be refactored @TODO
|
|
||||||
*/
|
|
||||||
private fun createSyntheticStackFrame(
|
private fun createSyntheticStackFrame(
|
||||||
descriptor: SuspendStackFrameDescriptor,
|
descriptor: SuspendStackFrameDescriptor,
|
||||||
pos: XSourcePosition,
|
pos: XSourcePosition,
|
||||||
@@ -206,6 +203,7 @@ class CreationCoroutineStackFrameItem(
|
|||||||
val location: Location
|
val location: Location
|
||||||
) : CoroutineStackFrameItem(frame, stackFrame) {
|
) : CoroutineStackFrameItem(frame, stackFrame) {
|
||||||
override fun location() = location
|
override fun location() = location
|
||||||
|
|
||||||
fun emptyDescriptor() =
|
fun emptyDescriptor() =
|
||||||
EmptyStackFrameDescriptor(stackTraceElement, frame)
|
EmptyStackFrameDescriptor(stackTraceElement, frame)
|
||||||
}
|
}
|
||||||
@@ -213,9 +211,13 @@ class CreationCoroutineStackFrameItem(
|
|||||||
class SuspendCoroutineStackFrameItem(
|
class SuspendCoroutineStackFrameItem(
|
||||||
frame: StackFrameProxyImpl,
|
frame: StackFrameProxyImpl,
|
||||||
val stackTraceElement: StackTraceElement,
|
val stackTraceElement: StackTraceElement,
|
||||||
stackFrame: XStackFrame
|
stackFrame: XStackFrame,
|
||||||
|
val lastObservedFrameFieldRef: ObjectReference
|
||||||
) : CoroutineStackFrameItem(frame, stackFrame) {
|
) : CoroutineStackFrameItem(frame, stackFrame) {
|
||||||
override fun location() = frame.location()
|
override fun location() = frame.location()
|
||||||
|
|
||||||
|
fun emptyDescriptor() =
|
||||||
|
EmptyStackFrameDescriptor(stackTraceElement, frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
class AsyncCoroutineStackFrameItem(
|
class AsyncCoroutineStackFrameItem(
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ data class CoroutineInfoData(
|
|||||||
val stackTrace: List<StackTraceElement>,
|
val stackTrace: List<StackTraceElement>,
|
||||||
// links to jdi.* references
|
// links to jdi.* references
|
||||||
val activeThread: ThreadReference? = null, // for suspended coroutines should be null
|
val activeThread: ThreadReference? = null, // for suspended coroutines should be null
|
||||||
val frame: ObjectReference?
|
val lastObservedFrameFieldRef: ObjectReference?
|
||||||
) {
|
) {
|
||||||
var stackFrameList = mutableListOf<CoroutineStackFrameItem>()
|
var stackFrameList = mutableListOf<CoroutineStackFrameItem>()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@ class CoroutineStackTraceData(
|
|||||||
val lookupContinuation = LookupContinuation(context, frame)
|
val lookupContinuation = LookupContinuation(context, frame)
|
||||||
|
|
||||||
// retrieve continuation only if suspend method
|
// retrieve continuation only if suspend method
|
||||||
val continuation = lookupContinuation.findContinuation(infoData)
|
val continuation = lookupContinuation.findContinuation(infoData.lastObservedFrameFieldRef)
|
||||||
|
|
||||||
return if (continuation is ObjectReference)
|
return if (continuation is ObjectReference)
|
||||||
SuspendStackFrameDescriptor(infoData, frame, proxy, continuation)
|
SuspendStackFrameDescriptor(infoData, frame, proxy, continuation)
|
||||||
|
|||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
package org.jetbrains.kotlin.idea.debugger.coroutine.override.com.intellij.ui;
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.text.StringUtil;
|
||||||
|
import com.intellij.ui.components.JBLabel;
|
||||||
|
import com.intellij.util.Function;
|
||||||
|
import com.intellij.util.ui.JBUI;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import sun.swing.DefaultLookup;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.plaf.basic.BasicHTML;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JBLabel-based (text and icon) list cell renderer.
|
||||||
|
*
|
||||||
|
* @see ColoredListCellRenderer for more complex SimpleColoredComponent-based variant.
|
||||||
|
*
|
||||||
|
* @author gregsh
|
||||||
|
*/
|
||||||
|
public abstract class SimpleListCellRenderer<T> extends JBLabel implements ListCellRenderer<T> {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static <T> SimpleListCellRenderer<T> create(@NotNull String nullValue, @NotNull Function<? super T, String> getText) {
|
||||||
|
return new SimpleListCellRenderer<T>() {
|
||||||
|
@Override
|
||||||
|
public void customize(JList<? extends T> list, T value, int index, boolean selected, boolean hasFocus) {
|
||||||
|
setText(value == null ? nullValue : getText.fun(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static <T> SimpleListCellRenderer<T> create(@NotNull Customizer<? super T> customizer) {
|
||||||
|
return new SimpleListCellRenderer<T>() {
|
||||||
|
@Override
|
||||||
|
public void customize(JList<? extends T> list, T value, int index, boolean selected, boolean hasFocus) {
|
||||||
|
customizer.customize(this, value, index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getListCellRendererComponent(JList<? extends T> list, T value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||||
|
setComponentOrientation(list.getComponentOrientation());
|
||||||
|
setBorder(JBUI.Borders.empty(1));
|
||||||
|
Color bg, fg;
|
||||||
|
JList.DropLocation dropLocation = list.getDropLocation();
|
||||||
|
if (dropLocation != null && !dropLocation.isInsert() && dropLocation.getIndex() == index) {
|
||||||
|
bg = DefaultLookup.getColor(this, ui, "List.dropCellBackground");
|
||||||
|
fg = DefaultLookup.getColor(this, ui, "List.dropCellForeground");
|
||||||
|
isSelected = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bg = isSelected ? list.getSelectionBackground() : list.getBackground();
|
||||||
|
fg = isSelected ? list.getSelectionForeground() : list.getForeground();
|
||||||
|
}
|
||||||
|
setBackground(bg);
|
||||||
|
setForeground(fg);
|
||||||
|
setFont(list.getFont());
|
||||||
|
setText("");
|
||||||
|
setIcon(null);
|
||||||
|
customize(list, value, index, isSelected, cellHasFocus);
|
||||||
|
setOpaque(isSelected);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void customize(JList<? extends T> list, T value, int index, boolean selected, boolean hasFocus);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getPreferredSize() {
|
||||||
|
if (StringUtil.isNotEmpty(getText())) {
|
||||||
|
return super.getPreferredSize();
|
||||||
|
}
|
||||||
|
setText(" ");
|
||||||
|
Dimension size = super.getPreferredSize();
|
||||||
|
setText("");
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Customizer<T> {
|
||||||
|
void customize(@NotNull JBLabel label, T value, int index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @formatter:off
|
||||||
|
@Override public void validate() {}
|
||||||
|
@Override public void invalidate() {}
|
||||||
|
@Override public void repaint() {}
|
||||||
|
@Override public void revalidate() {}
|
||||||
|
@Override public void repaint(long tm, int x, int y, int width, int height) {}
|
||||||
|
@Override public void repaint(Rectangle r) {}
|
||||||
|
@Override public void firePropertyChange(String propertyName, byte oldValue, byte newValue) {}
|
||||||
|
@Override public void firePropertyChange(String propertyName, char oldValue, char newValue) {}
|
||||||
|
@Override public void firePropertyChange(String propertyName, short oldValue, short newValue) {}
|
||||||
|
@Override public void firePropertyChange(String propertyName, int oldValue, int newValue) {}
|
||||||
|
@Override public void firePropertyChange(String propertyName, long oldValue, long newValue) {}
|
||||||
|
@Override public void firePropertyChange(String propertyName, float oldValue, float newValue) {}
|
||||||
|
@Override public void firePropertyChange(String propertyName, double oldValue, double newValue) {}
|
||||||
|
@Override public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
|
||||||
|
if (propertyName == "text"
|
||||||
|
|| ((propertyName == "font" || propertyName == "foreground")
|
||||||
|
&& oldValue != newValue
|
||||||
|
&& getClientProperty(BasicHTML.propertyKey) != null)) {
|
||||||
|
super.firePropertyChange(propertyName, oldValue, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
+9
-3
@@ -36,7 +36,11 @@ class AsyncStackTraceContext(
|
|||||||
fun getAsyncStackTraceIfAny() : List<CoroutineAsyncStackFrameItem> {
|
fun getAsyncStackTraceIfAny() : List<CoroutineAsyncStackFrameItem> {
|
||||||
val continuation = locateContinuation() ?: return emptyList()
|
val continuation = locateContinuation() ?: return emptyList()
|
||||||
val frames = mutableListOf<CoroutineAsyncStackFrameItem>()
|
val frames = mutableListOf<CoroutineAsyncStackFrameItem>()
|
||||||
collectFramesRecursively(continuation, frames)
|
try {
|
||||||
|
collectFramesRecursively(continuation, frames)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
log.error("Error while looking for variables.", e)
|
||||||
|
}
|
||||||
return frames
|
return frames
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,9 +75,9 @@ class AsyncStackTraceContext(
|
|||||||
val baseContinuationSupertype = findBaseContinuationSuperSupertype(continuationType) ?: return
|
val baseContinuationSupertype = findBaseContinuationSuperSupertype(continuationType) ?: return
|
||||||
|
|
||||||
val location = createLocation(continuation)
|
val location = createLocation(continuation)
|
||||||
val spilledVariables = getSpilledVariables(continuation) ?: emptyList()
|
|
||||||
|
|
||||||
location?.let {
|
location?.let {
|
||||||
|
val spilledVariables = getSpilledVariables(continuation) ?: emptyList()
|
||||||
consumer.add(CoroutineAsyncStackFrameItem(location, spilledVariables))
|
consumer.add(CoroutineAsyncStackFrameItem(location, spilledVariables))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +90,9 @@ class AsyncStackTraceContext(
|
|||||||
val instance = invokeGetStackTraceElement(continuation) ?: return null
|
val instance = invokeGetStackTraceElement(continuation) ?: return null
|
||||||
val className = context.invokeMethodAsString(instance, "getClassName") ?: return null
|
val className = context.invokeMethodAsString(instance, "getClassName") ?: return null
|
||||||
val methodName = context.invokeMethodAsString(instance, "getMethodName") ?: return null
|
val methodName = context.invokeMethodAsString(instance, "getMethodName") ?: return null
|
||||||
val lineNumber = context.invokeMethodAsInt(instance,"getLineNumber") ?: return null
|
val lineNumber = context.invokeMethodAsInt(instance,"getLineNumber")?.takeIf {
|
||||||
|
it >= 0
|
||||||
|
} ?: return null
|
||||||
val locationClass = context.findClassSafe(className) ?: return null
|
val locationClass = context.findClassSafe(className) ?: return null
|
||||||
log.warn("Got location of ${className}.${methodName}:${lineNumber} in ${locationClass}")
|
log.warn("Got location of ${className}.${methodName}:${lineNumber} in ${locationClass}")
|
||||||
return GeneratedLocation(context.debugProcess, locationClass, methodName, lineNumber)
|
return GeneratedLocation(context.debugProcess, locationClass, methodName, lineNumber)
|
||||||
|
|||||||
+13
-2
@@ -35,11 +35,11 @@ class LookupContinuation(val context: ExecutionContext, val frame: StackTraceEle
|
|||||||
* Gets current CoroutineInfo.lastObservedFrame and finds next frames in it until null or needed stackTraceElement is found
|
* Gets current CoroutineInfo.lastObservedFrame and finds next frames in it until null or needed stackTraceElement is found
|
||||||
* @return null if matching continuation is not found or is not BaseContinuationImpl
|
* @return null if matching continuation is not found or is not BaseContinuationImpl
|
||||||
*/
|
*/
|
||||||
fun findContinuation(infoData: CoroutineInfoData): ObjectReference? {
|
fun findContinuation(initialContinuation: ObjectReference?): ObjectReference? {
|
||||||
if (!isApplicable())
|
if (!isApplicable())
|
||||||
return null
|
return null
|
||||||
|
|
||||||
var continuation = infoData.frame ?: return null
|
var continuation = initialContinuation ?: return null
|
||||||
val baseType = "kotlin.coroutines.jvm.internal.BaseContinuationImpl"
|
val baseType = "kotlin.coroutines.jvm.internal.BaseContinuationImpl"
|
||||||
val getTrace = (continuation.type() as ClassType).concreteMethodByName(
|
val getTrace = (continuation.type() as ClassType).concreteMethodByName(
|
||||||
"getStackTraceElement",
|
"getStackTraceElement",
|
||||||
@@ -81,4 +81,15 @@ class LookupContinuation(val context: ExecutionContext, val frame: StackTraceEle
|
|||||||
val next = type.concreteMethodByName("getCompletion", "()Lkotlin/coroutines/Continuation;")
|
val next = type.concreteMethodByName("getCompletion", "()Lkotlin/coroutines/Continuation;")
|
||||||
return context.invokeMethod(continuation, next, emptyList()) as? ObjectReference
|
return context.invokeMethod(continuation, next, emptyList()) as? ObjectReference
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun findGetStackTraceElementMethodRef(continuation: ObjectReference): Method =
|
||||||
|
(continuation.type() as ClassType).concreteMethodByName("getStackTraceElement", "()Ljava/lang/StackTraceElement;")
|
||||||
|
|
||||||
|
fun createAsyncStackTraceContext(stackTraceElementMethodRef: Method) =
|
||||||
|
AsyncStackTraceContext(context, stackTraceElementMethodRef)
|
||||||
|
|
||||||
|
fun createAsyncStackTraceContext(continuation: ObjectReference): AsyncStackTraceContext? {
|
||||||
|
val getStackTraceElementMethodRef = findGetStackTraceElementMethodRef(continuation)
|
||||||
|
return createAsyncStackTraceContext(getStackTraceElementMethodRef)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+3
-3
@@ -13,7 +13,7 @@ public class Utils {
|
|||||||
Object instance,
|
Object instance,
|
||||||
String methodName,
|
String methodName,
|
||||||
Object... args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
|
Object... args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
Class[] classes = new Class[args.length];
|
Class[] classes = new Class[args.length];
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
if(args[i] instanceof ReflectionSpecificType)
|
if(args[i] instanceof ReflectionSpecificType)
|
||||||
@@ -23,10 +23,10 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
Method method = instance.getClass().getDeclaredMethod(methodName, classes);
|
Method method = instance.getClass().getDeclaredMethod(methodName, classes);
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
Object r = method.invoke(instance, args);
|
return method.invoke(instance, args);
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
public interface ReflectionSpecificType {
|
public interface ReflectionSpecificType {
|
||||||
Class specificClass();
|
Class specificClass();
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -235,8 +235,8 @@ class CoroutineDumpPanel(project: Project, consoleView: ConsoleView, toolbarActi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private inner class MergeStackTracesAction : ToggleAction(
|
private inner class MergeStackTracesAction : ToggleAction(
|
||||||
"Merge Identical Stacktraces",
|
"Merge Identical Stacktrace",
|
||||||
"Group coroutines with identical stacktraces",
|
"Group coroutines with identical stacktrace",
|
||||||
AllIcons.Actions.Collapseall
|
AllIcons.Actions.Collapseall
|
||||||
), DumbAware {
|
), DumbAware {
|
||||||
|
|
||||||
|
|||||||
+23
-10
@@ -8,18 +8,25 @@ package org.jetbrains.kotlin.idea.debugger.coroutine.view
|
|||||||
import com.intellij.debugger.impl.DebuggerUtilsEx
|
import com.intellij.debugger.impl.DebuggerUtilsEx
|
||||||
import com.intellij.debugger.settings.ThreadsViewSettings
|
import com.intellij.debugger.settings.ThreadsViewSettings
|
||||||
import com.intellij.icons.AllIcons
|
import com.intellij.icons.AllIcons
|
||||||
|
import com.intellij.ide.highlighter.JavaHighlightingColors
|
||||||
|
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors
|
||||||
import com.intellij.openapi.editor.HighlighterColors
|
import com.intellij.openapi.editor.HighlighterColors
|
||||||
import com.intellij.openapi.editor.colors.CodeInsightColors
|
import com.intellij.openapi.editor.colors.CodeInsightColors
|
||||||
|
import com.intellij.openapi.editor.colors.EditorColors
|
||||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||||
|
import com.intellij.openapi.editor.markup.TextAttributes
|
||||||
import com.intellij.ui.ColoredTextContainer
|
import com.intellij.ui.ColoredTextContainer
|
||||||
import com.intellij.ui.SimpleColoredComponent
|
import com.intellij.ui.SimpleColoredComponent
|
||||||
import com.intellij.ui.SimpleTextAttributes
|
import com.intellij.ui.SimpleTextAttributes
|
||||||
import com.intellij.xdebugger.frame.presentation.XValuePresentation
|
import com.intellij.xdebugger.frame.presentation.XValuePresentation
|
||||||
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil
|
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil
|
||||||
import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants
|
import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants
|
||||||
|
import com.intellij.xdebugger.ui.DebuggerColors
|
||||||
import com.sun.jdi.Location
|
import com.sun.jdi.Location
|
||||||
import com.sun.jdi.ReferenceType
|
import com.sun.jdi.ReferenceType
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData
|
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
|
||||||
|
import java.awt.Color
|
||||||
import javax.swing.Icon
|
import javax.swing.Icon
|
||||||
|
|
||||||
class SimpleColoredTextIcon(val icon: Icon?, val hasChildrens: Boolean) {
|
class SimpleColoredTextIcon(val icon: Icon?, val hasChildrens: Boolean) {
|
||||||
@@ -45,15 +52,19 @@ class SimpleColoredTextIcon(val icon: Icon?, val hasChildrens: Boolean) {
|
|||||||
for (i in 0 until size) {
|
for (i in 0 until size) {
|
||||||
val text: String = texts.get(i)
|
val text: String = texts.get(i)
|
||||||
val attribute: TextAttributesKey = textKeyAttributes.get(i)
|
val attribute: TextAttributesKey = textKeyAttributes.get(i)
|
||||||
|
val simpleTextAttrinbute = toSimpleTextAttribute(attribute)
|
||||||
|
|
||||||
component.append(text, when(attribute) {
|
component.append(text, simpleTextAttrinbute)
|
||||||
CoroutineDebuggerColors.REGULAR_ATTRIBUTES -> SimpleTextAttributes.REGULAR_ATTRIBUTES
|
|
||||||
CoroutineDebuggerColors.VALUE_ATTRIBUTES -> XDebuggerUIConstants.VALUE_NAME_ATTRIBUTES
|
|
||||||
else -> SimpleTextAttributes.REGULAR_ATTRIBUTES
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun toSimpleTextAttribute(attribute: TextAttributesKey) =
|
||||||
|
when (attribute) {
|
||||||
|
CoroutineDebuggerColors.REGULAR_ATTRIBUTES -> SimpleTextAttributes.REGULAR_ATTRIBUTES
|
||||||
|
CoroutineDebuggerColors.VALUE_ATTRIBUTES -> XDebuggerUIConstants.VALUE_NAME_ATTRIBUTES
|
||||||
|
else -> SimpleTextAttributes.REGULAR_ATTRIBUTES
|
||||||
|
}
|
||||||
|
|
||||||
fun forEachTextBlock(f: (Pair<String, TextAttributesKey>) -> Unit) {
|
fun forEachTextBlock(f: (Pair<String, TextAttributesKey>) -> Unit) {
|
||||||
for (pair in texts zip textKeyAttributes)
|
for (pair in texts zip textKeyAttributes)
|
||||||
f(pair)
|
f(pair)
|
||||||
@@ -83,14 +94,13 @@ class SimpleColoredTextIcon(val icon: Icon?, val hasChildrens: Boolean) {
|
|||||||
|
|
||||||
interface CoroutineDebuggerColors {
|
interface CoroutineDebuggerColors {
|
||||||
companion object {
|
companion object {
|
||||||
val REGULAR_ATTRIBUTES =
|
val REGULAR_ATTRIBUTES = HighlighterColors.TEXT
|
||||||
TextAttributesKey.createTextAttributesKey("REGULAR_ATTRIBUTES", HighlighterColors.NO_HIGHLIGHTING)
|
val VALUE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("KOTLIN_COROUTINE_DEBUGGER_VALUE", HighlighterColors.TEXT)
|
||||||
val VALUE_ATTRIBUTES =
|
|
||||||
TextAttributesKey.createTextAttributesKey("VALUE_ATTRIBUTES", CodeInsightColors.WARNINGS_ATTRIBUTES)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimpleColoredTextIconPresentationRenderer {
|
class SimpleColoredTextIconPresentationRenderer {
|
||||||
|
val log by logger
|
||||||
private val settings: ThreadsViewSettings = ThreadsViewSettings.getInstance()
|
private val settings: ThreadsViewSettings = ThreadsViewSettings.getInstance()
|
||||||
|
|
||||||
fun render(infoData: CoroutineInfoData): SimpleColoredTextIcon {
|
fun render(infoData: CoroutineInfoData): SimpleColoredTextIcon {
|
||||||
@@ -157,7 +167,10 @@ class SimpleColoredTextIconPresentationRenderer {
|
|||||||
}
|
}
|
||||||
if (settings.SHOW_SOURCE_NAME) {
|
if (settings.SHOW_SOURCE_NAME) {
|
||||||
label.append(", ")
|
label.append(", ")
|
||||||
val sourceName = DebuggerUtilsEx.getSourceName(location) { e: Throwable? -> "Unknown Source" }
|
val sourceName = DebuggerUtilsEx.getSourceName(location) { e: Throwable? ->
|
||||||
|
log.error("Error while trying to resolve sourceName for location", e, location.toString())
|
||||||
|
"Unknown Source"
|
||||||
|
}
|
||||||
label.append(sourceName)
|
label.append(sourceName)
|
||||||
}
|
}
|
||||||
return label
|
return label
|
||||||
|
|||||||
+97
-59
@@ -5,23 +5,35 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.coroutine.view
|
package org.jetbrains.kotlin.idea.debugger.coroutine.view
|
||||||
|
|
||||||
import com.intellij.debugger.DebuggerManagerEx
|
|
||||||
import com.intellij.debugger.engine.DebugProcessImpl
|
import com.intellij.debugger.engine.DebugProcessImpl
|
||||||
import com.intellij.debugger.engine.JavaDebugProcess
|
import com.intellij.debugger.engine.JavaDebugProcess
|
||||||
import com.intellij.debugger.engine.JavaExecutionStack
|
import com.intellij.debugger.engine.JavaExecutionStack
|
||||||
import com.intellij.debugger.engine.SuspendContextImpl
|
import com.intellij.debugger.engine.SuspendContextImpl
|
||||||
|
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||||
|
import com.intellij.debugger.jdi.StackFrameProxyImpl
|
||||||
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
|
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
|
||||||
|
import com.intellij.ide.CommonActionsManager
|
||||||
import com.intellij.openapi.Disposable
|
import com.intellij.openapi.Disposable
|
||||||
|
import com.intellij.openapi.actionSystem.ActionManager
|
||||||
|
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||||
|
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||||
|
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.ui.ComboBox
|
||||||
import com.intellij.psi.JavaPsiFacade
|
import com.intellij.psi.JavaPsiFacade
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.ui.CaptionPanel
|
||||||
|
import com.intellij.ui.ComboboxSpeedSearch
|
||||||
import com.intellij.ui.DoubleClickListener
|
import com.intellij.ui.DoubleClickListener
|
||||||
import com.intellij.ui.OnePixelSplitter
|
import com.intellij.ui.border.CustomLineBorder
|
||||||
|
import com.intellij.ui.components.JBLabel
|
||||||
|
import com.intellij.ui.components.panels.Wrapper
|
||||||
import com.intellij.util.SingleAlarm
|
import com.intellij.util.SingleAlarm
|
||||||
import com.intellij.xdebugger.XDebugSession
|
import com.intellij.xdebugger.XDebugSession
|
||||||
import com.intellij.xdebugger.XDebuggerUtil
|
import com.intellij.xdebugger.XDebuggerUtil
|
||||||
import com.intellij.xdebugger.XSourcePosition
|
import com.intellij.xdebugger.XSourcePosition
|
||||||
import com.intellij.xdebugger.frame.*
|
import com.intellij.xdebugger.frame.*
|
||||||
|
import com.intellij.xdebugger.impl.actions.XDebuggerActions
|
||||||
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil
|
import com.intellij.xdebugger.impl.ui.DebuggerUIUtil
|
||||||
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree
|
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree
|
||||||
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreePanel
|
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreePanel
|
||||||
@@ -29,31 +41,34 @@ import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeRestorer
|
|||||||
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState
|
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState
|
||||||
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueContainerNode
|
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueContainerNode
|
||||||
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl
|
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl
|
||||||
import com.sun.jdi.ClassType
|
|
||||||
import javaslang.control.Either
|
import javaslang.control.Either
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineAsyncStackTraceProvider
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineDebuggerContentInfo
|
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineDebuggerContentInfo
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineDebuggerContentInfo.Companion.XCOROUTINE_POPUP_ACTION_GROUP
|
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineDebuggerContentInfo.Companion.XCOROUTINE_POPUP_ACTION_GROUP
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.coroutine.VersionedImplementationProvider
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.command.*
|
import org.jetbrains.kotlin.idea.debugger.coroutine.command.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData
|
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.SuspendStackFrameDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.data.SyntheticStackFrame
|
import org.jetbrains.kotlin.idea.debugger.coroutine.data.SyntheticStackFrame
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ApplicationThreadExecutor
|
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.*
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.AsyncStackTraceContext
|
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CreateContentParams
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.CoroutineDebugProbesProxy
|
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CreateContentParamsProvider
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ManagerThreadExecutor
|
import org.jetbrains.kotlin.idea.debugger.coroutine.util.XDebugSessionListenerProvider
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.util.*
|
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
import org.jetbrains.kotlin.idea.debugger.evaluate.ExecutionContext
|
||||||
|
import java.awt.BorderLayout
|
||||||
import java.awt.event.KeyAdapter
|
import java.awt.event.KeyAdapter
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
import java.awt.event.MouseEvent
|
import java.awt.event.MouseEvent
|
||||||
|
import javax.swing.JPanel
|
||||||
|
|
||||||
|
|
||||||
class XCoroutineView(val project: Project, val session: XDebugSession) :
|
class XCoroutineView(val project: Project, val session: XDebugSession) :
|
||||||
Disposable, XDebugSessionListenerProvider, CreateContentParamsProvider {
|
Disposable, XDebugSessionListenerProvider, CreateContentParamsProvider {
|
||||||
val log by logger
|
val log by logger
|
||||||
val splitter = OnePixelSplitter("SomeKey", 0.25f)
|
val versionedImplementationProvider = VersionedImplementationProvider()
|
||||||
|
|
||||||
|
val mainPanel = JPanel(BorderLayout())
|
||||||
|
val someCombobox = ComboBox<String>()
|
||||||
val panel = XDebuggerTreePanel(project, session.debugProcess.editorsProvider, this, null, XCOROUTINE_POPUP_ACTION_GROUP, null)
|
val panel = XDebuggerTreePanel(project, session.debugProcess.editorsProvider, this, null, XCOROUTINE_POPUP_ACTION_GROUP, null)
|
||||||
val alarm = SingleAlarm(Runnable { resetRoot() }, VIEW_CLEAR_DELAY, this)
|
val alarm = SingleAlarm(Runnable { resetRoot() }, VIEW_CLEAR_DELAY, this)
|
||||||
val javaDebugProcess = session.debugProcess as JavaDebugProcess
|
val javaDebugProcess = session.debugProcess as JavaDebugProcess
|
||||||
@@ -70,11 +85,36 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
splitter.firstComponent = panel.mainPanel
|
someCombobox.setRenderer(versionedImplementationProvider.comboboxListCellRenderer())
|
||||||
|
object : ComboboxSpeedSearch(someCombobox) {
|
||||||
|
override fun getElementText(element: Any?): String? {
|
||||||
|
return element.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
someCombobox.addItem(null)
|
||||||
|
val myToolbar = createToolbar()
|
||||||
|
val myThreadsPanel = Wrapper()
|
||||||
|
myThreadsPanel.setBorder(CustomLineBorder(CaptionPanel.CNT_ACTIVE_BORDER_COLOR, 0, 0, 1, 0))
|
||||||
|
myThreadsPanel.add(myToolbar?.getComponent(), BorderLayout.EAST)
|
||||||
|
myThreadsPanel.add(someCombobox, BorderLayout.CENTER)
|
||||||
|
mainPanel.add(myThreadsPanel, BorderLayout.NORTH)
|
||||||
|
mainPanel.add(panel.mainPanel, BorderLayout.CENTER)
|
||||||
selectedNodeListener.installOn()
|
selectedNodeListener.installOn()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun createToolbar(): ActionToolbarImpl? {
|
||||||
|
val framesGroup = DefaultActionGroup()
|
||||||
|
val actionsManager = CommonActionsManager.getInstance()
|
||||||
|
framesGroup
|
||||||
|
.addAll(ActionManager.getInstance().getAction(XDebuggerActions.FRAMES_TOP_TOOLBAR_GROUP))
|
||||||
|
val toolbar = ActionManager.getInstance().createActionToolbar(
|
||||||
|
ActionPlaces.DEBUGGER_TOOLBAR, framesGroup, true
|
||||||
|
) as ActionToolbarImpl
|
||||||
|
toolbar.setReservePlaceAutoPopupIcon(false)
|
||||||
|
return toolbar
|
||||||
|
}
|
||||||
|
|
||||||
fun saveState() {
|
fun saveState() {
|
||||||
DebuggerUIUtil.invokeLater {
|
DebuggerUIUtil.invokeLater {
|
||||||
if (! (panel.tree.root is EmptyNode)) {
|
if (! (panel.tree.root is EmptyNode)) {
|
||||||
@@ -113,7 +153,7 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
override fun createContentParams(): CreateContentParams =
|
override fun createContentParams(): CreateContentParams =
|
||||||
CreateContentParams(
|
CreateContentParams(
|
||||||
CoroutineDebuggerContentInfo.XCOROUTINE_THREADS_CONTENT,
|
CoroutineDebuggerContentInfo.XCOROUTINE_THREADS_CONTENT,
|
||||||
splitter,
|
mainPanel,
|
||||||
KotlinBundle.message("debugger.session.tab.xcoroutine.title"),
|
KotlinBundle.message("debugger.session.tab.xcoroutine.title"),
|
||||||
null,
|
null,
|
||||||
panel.tree
|
panel.tree
|
||||||
@@ -148,7 +188,7 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
}
|
}
|
||||||
node.addChildren(children, true)
|
node.addChildren(children, true)
|
||||||
} else {
|
} else {
|
||||||
node.addChildren(XValueChildrenList.singleton(ErrorNode("Error occured while fetching information")), true)
|
node.addChildren(XValueChildrenList.singleton(ErrorNode("Error occurs while fetching information")), true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,24 +248,33 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
applyRenderer(node, presentation)
|
applyRenderer(node, presentation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class KeyMouseEvent(val keyEvent: KeyEvent?, val mouseEvent: MouseEvent?) {
|
||||||
|
constructor(keyEvent: KeyEvent) : this(keyEvent, null)
|
||||||
|
constructor(mouseEvent: MouseEvent) : this(null, mouseEvent)
|
||||||
|
|
||||||
|
fun isKeyEvent() = keyEvent != null
|
||||||
|
|
||||||
|
fun isMouseEvent() = mouseEvent != null
|
||||||
|
}
|
||||||
|
|
||||||
inner class XDebuggerTreeSelectedNodeListener(val tree: XDebuggerTree) {
|
inner class XDebuggerTreeSelectedNodeListener(val tree: XDebuggerTree) {
|
||||||
|
|
||||||
fun installOn() {
|
fun installOn() {
|
||||||
object : DoubleClickListener() {
|
object : DoubleClickListener() {
|
||||||
override fun onDoubleClick(e: MouseEvent) =
|
override fun onDoubleClick(e: MouseEvent) =
|
||||||
nodeSelected(Either.left(e))
|
nodeSelected(KeyMouseEvent(e))
|
||||||
}.installOn(tree)
|
}.installOn(tree)
|
||||||
|
|
||||||
tree.addKeyListener(object : KeyAdapter() {
|
tree.addKeyListener(object : KeyAdapter() {
|
||||||
override fun keyPressed(e: KeyEvent) {
|
override fun keyPressed(e: KeyEvent) {
|
||||||
val key = e.keyCode
|
val key = e.keyCode
|
||||||
if (key == KeyEvent.VK_ENTER || key == KeyEvent.VK_SPACE || key == KeyEvent.VK_RIGHT)
|
if (key == KeyEvent.VK_ENTER || key == KeyEvent.VK_SPACE || key == KeyEvent.VK_RIGHT)
|
||||||
nodeSelected(Either.right(e))
|
nodeSelected(KeyMouseEvent(e))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nodeSelected(event: Either<MouseEvent, KeyEvent>) : Boolean {
|
fun nodeSelected(event: KeyMouseEvent) : Boolean {
|
||||||
val selectedNodes = tree.getSelectedNodes(XValueNodeImpl::class.java, null)
|
val selectedNodes = tree.getSelectedNodes(XValueNodeImpl::class.java, null)
|
||||||
if (selectedNodes.size == 1) {
|
if (selectedNodes.size == 1) {
|
||||||
val node = selectedNodes[0]
|
val node = selectedNodes[0]
|
||||||
@@ -242,21 +291,17 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
is CreationCoroutineStackFrameItem -> {
|
is CreationCoroutineStackFrameItem -> {
|
||||||
val position = getPosition(frame.stackTraceElement) ?: return false
|
val position = getPosition(frame.stackTraceElement) ?: return false
|
||||||
val threadProxy = threadSuspendContext.thread as ThreadReferenceProxyImpl
|
val threadProxy = threadSuspendContext.thread as ThreadReferenceProxyImpl
|
||||||
val stackFrame =
|
createStackAndSetFrame(threadProxy, { SyntheticStackFrame(frame.emptyDescriptor(), emptyList(), position) })
|
||||||
createStackAndSetFrame(threadProxy, { SyntheticStackFrame(frame.emptyDescriptor(), emptyList(), position) })
|
|
||||||
}
|
}
|
||||||
is SuspendCoroutineStackFrameItem -> {
|
is SuspendCoroutineStackFrameItem -> {
|
||||||
val position = getPosition(frame.stackTraceElement) ?: return false
|
val threadProxy = threadSuspendContext.thread as ThreadReferenceProxyImpl
|
||||||
|
val executionContext = executionContext(threadSuspendContext, frame.frame)
|
||||||
|
createStackAndSetFrame(threadProxy, { createSyntheticStackFrame(executionContext, frame) })
|
||||||
}
|
}
|
||||||
is AsyncCoroutineStackFrameItem -> {
|
is AsyncCoroutineStackFrameItem -> {
|
||||||
|
|
||||||
}
|
}
|
||||||
// else -> {
|
else -> {
|
||||||
// val (stack, stackFrame) = createSyntheticStackFrame(descriptor, pos) ?: return
|
}
|
||||||
// val action: () -> Unit = { context.debuggerSession?.xDebugSession?.setCurrentStackFrame(stack, stackFrame) }
|
|
||||||
// - ApplicationManager.getApplication()
|
|
||||||
// - .invokeLater(action, ModalityState.stateForComponent(this@CoroutinesDebuggerTree))
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,21 +309,22 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createSyntheticStackFrame() {}
|
fun createStackAndSetFrame(threadReferenceProxy: ThreadReferenceProxyImpl, stackFrameProvider: () -> XStackFrame?, isCurrentContext: Boolean = false) {
|
||||||
|
|
||||||
fun createStackAndSetFrame(threadReferenceProxy: ThreadReferenceProxyImpl, stackFrameProvider: () -> XStackFrame, isCurrentContext: Boolean = false) {
|
|
||||||
val threadSuspendContext = session.suspendContext as SuspendContextImpl
|
val threadSuspendContext = session.suspendContext as SuspendContextImpl
|
||||||
managerThreadExecutor.on(threadSuspendContext).schedule {
|
managerThreadExecutor.on(threadSuspendContext).schedule {
|
||||||
val stackFrame = stackFrameProvider.invoke()
|
val stackFrame = stackFrameProvider.invoke()
|
||||||
val executionStack = createExecutionStack(threadReferenceProxy, isCurrentContext)
|
if(stackFrame is XStackFrame) {
|
||||||
applicationThreadExecutor.schedule(
|
val executionStack = createExecutionStack(threadReferenceProxy, isCurrentContext)
|
||||||
{
|
applicationThreadExecutor.schedule(
|
||||||
session.setCurrentStackFrame(executionStack, stackFrame)
|
{
|
||||||
}, panel.tree)
|
session.setCurrentStackFrame(executionStack, stackFrame)
|
||||||
|
}, panel.tree
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createExecutionStack(proxy: ThreadReferenceProxyImpl, isCurrentContext: Boolean = false) : XExecutionStack {
|
private fun createExecutionStack(proxy: ThreadReferenceProxyImpl, isCurrentContext: Boolean = false) : XExecutionStack {
|
||||||
val executionStack = CoroutineDebuggerExecutionStack(proxy, isCurrentContext)
|
val executionStack = CoroutineDebuggerExecutionStack(proxy, isCurrentContext)
|
||||||
executionStack.initTopFrame()
|
executionStack.initTopFrame()
|
||||||
return executionStack
|
return executionStack
|
||||||
@@ -300,30 +346,22 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
|
|||||||
return XDebuggerUtil.getInstance().createPosition(classFile, lineNumber)
|
return XDebuggerUtil.getInstance().createPosition(classFile, lineNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun executionContext(suspendContext: SuspendContextImpl, frameProxy: StackFrameProxyImpl) : ExecutionContext {
|
||||||
|
val evaluationContextImpl = EvaluationContextImpl(suspendContext, frameProxy)
|
||||||
|
return ExecutionContext(evaluationContextImpl, frameProxy)
|
||||||
|
}
|
||||||
|
|
||||||
private fun createSyntheticStackFrame(
|
private fun createSyntheticStackFrame(
|
||||||
descriptor: SuspendStackFrameDescriptor,
|
executionContext: ExecutionContext,
|
||||||
pos: XSourcePosition
|
frame: SuspendCoroutineStackFrameItem
|
||||||
): Pair<XExecutionStack, SyntheticStackFrame>? {
|
): SyntheticStackFrame? {
|
||||||
val context = DebuggerManagerEx.getInstanceEx(project).context
|
val position = getPosition(frame.stackTraceElement) ?: return null
|
||||||
val suspendContext = context.suspendContext ?: return null
|
val lookupContinuation = LookupContinuation(executionContext, frame.stackTraceElement)
|
||||||
val proxy = suspendContext.thread ?: return null
|
val continuation = lookupContinuation.findContinuation(frame.lastObservedFrameFieldRef) ?: return null
|
||||||
val executionStack = JavaExecutionStack(proxy, suspendContext.debugProcess, false)
|
|
||||||
executionStack.initTopFrame()
|
val asyncStackTraceContext = lookupContinuation.createAsyncStackTraceContext(continuation)
|
||||||
val evalContext = context.createEvaluationContext()
|
val vars = asyncStackTraceContext?.getSpilledVariables(continuation) ?: return null
|
||||||
val frameProxy = evalContext?.frameProxy ?: return null
|
return SyntheticStackFrame(frame.emptyDescriptor(), vars, position)
|
||||||
val execContext = ExecutionContext(evalContext, frameProxy)
|
|
||||||
val continuation = descriptor.continuation // guaranteed that it is a BaseContinuationImpl
|
|
||||||
val aMethod = (continuation.type() as ClassType).concreteMethodByName(
|
|
||||||
"getStackTraceElement",
|
|
||||||
"()Ljava/lang/StackTraceElement;"
|
|
||||||
)
|
|
||||||
val vars = with(CoroutineAsyncStackTraceProvider()) {
|
|
||||||
AsyncStackTraceContext(
|
|
||||||
execContext,
|
|
||||||
aMethod
|
|
||||||
).getSpilledVariables(continuation)
|
|
||||||
} ?: return null
|
|
||||||
return executionStack to SyntheticStackFrame(descriptor, vars, pos)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-1
@@ -10,12 +10,14 @@ import com.intellij.debugger.engine.JavaValue
|
|||||||
import com.intellij.debugger.memory.utils.StackFrameItem
|
import com.intellij.debugger.memory.utils.StackFrameItem
|
||||||
import com.intellij.execution.process.ProcessOutputTypes
|
import com.intellij.execution.process.ProcessOutputTypes
|
||||||
import com.intellij.openapi.extensions.Extensions
|
import com.intellij.openapi.extensions.Extensions
|
||||||
|
import io.ktor.util.findAllSupertypes
|
||||||
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineAsyncStackTraceProvider
|
import org.jetbrains.kotlin.idea.debugger.coroutine.CoroutineAsyncStackTraceProvider
|
||||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferences
|
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferences
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
import org.jetbrains.kotlin.utils.getSafe
|
import org.jetbrains.kotlin.utils.getSafe
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
|
import java.lang.reflect.Field
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
|
|
||||||
abstract class AbstractAsyncStackTraceTest : KotlinDescriptorTestCaseWithStepping() {
|
abstract class AbstractAsyncStackTraceTest : KotlinDescriptorTestCaseWithStepping() {
|
||||||
@@ -81,9 +83,10 @@ abstract class AbstractAsyncStackTraceTest : KotlinDescriptorTestCaseWithSteppin
|
|||||||
appendln("Async stack trace:")
|
appendln("Async stack trace:")
|
||||||
for (item in trace) {
|
for (item in trace) {
|
||||||
append(MARGIN).appendln(item.toString())
|
append(MARGIN).appendln(item.toString())
|
||||||
|
val declaredFields = listDeclaredFields(item.javaClass)
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val variablesField = item.javaClass.declaredFields
|
val variablesField = declaredFields
|
||||||
.first { !Modifier.isStatic(it.modifiers) && it.type == List::class.java }
|
.first { !Modifier.isStatic(it.modifiers) && it.type == List::class.java }
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -100,4 +103,14 @@ abstract class AbstractAsyncStackTraceTest : KotlinDescriptorTestCaseWithSteppin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun listDeclaredFields(cls: Class<in Any>): MutableList<Field> {
|
||||||
|
var clazz = cls
|
||||||
|
val declaredFields = mutableListOf<Field>()
|
||||||
|
while (clazz != Class.forName("java.lang.Object")) {
|
||||||
|
declaredFields.addAll(clazz.declaredFields)
|
||||||
|
clazz = clazz.superclass
|
||||||
|
}
|
||||||
|
return declaredFields
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -86,4 +86,9 @@
|
|||||||
<option name="FOREGROUND" value="20999d" />
|
<option name="FOREGROUND" value="20999d" />
|
||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
|
<option name="KOTLIN_COROUTINE_DEBUGGER_VALUE">
|
||||||
|
<value>
|
||||||
|
<option name="FOREGROUND" value="FF8C8B" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
</list>
|
</list>
|
||||||
|
|||||||
@@ -76,4 +76,9 @@
|
|||||||
<option name="ERROR_STRIPE_COLOR" value="ff0000"/>
|
<option name="ERROR_STRIPE_COLOR" value="ff0000"/>
|
||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
|
<option name="KOTLIN_COROUTINE_DEBUGGER_VALUE">
|
||||||
|
<value>
|
||||||
|
<option name="FOREGROUND" value="840000" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
</list>
|
</list>
|
||||||
|
|||||||
Reference in New Issue
Block a user