(CoroutineDebugger) 201 compatibility fix

#KT-39143 fixed
This commit is contained in:
Vladimir Ilmov
2020-05-21 20:11:09 +02:00
parent 7a70f8d29e
commit 545fdb96d5
33 changed files with 741 additions and 620 deletions
@@ -0,0 +1,16 @@
# Coroutine Debugger support
## Java thread stack with coroutine information
1. Real frames
1. Coroutine 'preflight' frame: [SuspendExitMode](src/org/jetbrains/kotlin/idea/debugger/coroutine/data/SuspendExitMode.kt)
is `SUSPEND_LAMBDA` or `SUSPEND_METHOD_PARAMETER`. [Location.isPreflight](org/jetbrains/kotlin/idea/debugger/coroutine/util/CoroutineUtils.kt)
1. One or more frames (skipped in debugger)
1. Coroutine 'starting' frame: [SuspendExitMode](src/org/jetbrains/kotlin/idea/debugger/coroutine/data/SuspendExitMode.kt) `SUSPEND_METHOD` frame
1. Restored from coroutine information frames
1. Real frames after the 'starting' frame
1. Creation frames (only exists if coroutine agent enabled)
## Debugger interface
Debugger works as a combination of PositionManager and AsyncStackTraceProvider.
Once the 'preflight' signature frame found PositionManager forms a 'preflight' frame with coroutine information which gets
processed with AsyncStackTraceProvider.
@@ -9,7 +9,7 @@ import com.intellij.debugger.engine.AsyncStackTraceProvider
import com.intellij.debugger.engine.JavaStackFrame
import com.intellij.debugger.engine.SuspendContextImpl
import com.intellij.debugger.memory.utils.StackFrameItem
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutinePreflightStackFrame
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutinePreflightFrame
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineStackFrameItem
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CoroutineFrameBuilder
import org.jetbrains.kotlin.idea.debugger.coroutine.util.threadAndContextSupportsEvaluation
@@ -19,18 +19,19 @@ class CoroutineAsyncStackTraceProvider : AsyncStackTraceProvider {
override fun getAsyncStackTrace(stackFrame: JavaStackFrame, suspendContext: SuspendContextImpl): List<StackFrameItem>? {
val stackFrameList = hopelessAware {
if (stackFrame is CoroutinePreflightStackFrame)
if (stackFrame is CoroutinePreflightFrame)
processPreflight(stackFrame, suspendContext)
else
null
}
return if (stackFrameList == null || stackFrameList.isEmpty())
null
else stackFrameList
else
stackFrameList
}
private fun processPreflight(
preflightFrame: CoroutinePreflightStackFrame,
preflightFrame: CoroutinePreflightFrame,
suspendContext: SuspendContextImpl
): List<CoroutineStackFrameItem>? {
val resumeWithFrame = preflightFrame.threadPreCoroutineFrames.firstOrNull()
@@ -41,15 +42,8 @@ class CoroutineAsyncStackTraceProvider : AsyncStackTraceProvider {
)
) {
val doubleFrameList = CoroutineFrameBuilder.build(preflightFrame, suspendContext)
val resultList = doubleFrameList.stackTrace + doubleFrameList.creationStackTrace
return PreflightProvider(preflightFrame, resultList)
return doubleFrameList.allFrames()
}
return null
}
}
class PreflightProvider(private val preflight: CoroutinePreflightStackFrame, stackFrames: List<CoroutineStackFrameItem>) :
List<CoroutineStackFrameItem> by stackFrames {
fun getPreflight() =
preflight
}
@@ -1,117 +0,0 @@
/*
* 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.data
import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.memory.utils.StackFrameItem
import com.intellij.debugger.ui.impl.watch.MethodsTracker
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
import com.intellij.xdebugger.frame.XNamedValue
import com.sun.jdi.Location
import org.jetbrains.kotlin.idea.debugger.*
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isInvokeSuspend
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
/**
* Creation frame of coroutine either in RUNNING or SUSPENDED state.
*/
class CreationCoroutineStackFrameItem(
val stackTraceElement: StackTraceElement,
location: Location,
val first: Boolean
) : CoroutineStackFrameItem(location, emptyList()) {
fun descriptor(frame: StackFrameProxyImpl) =
RestoredStackFrameDescriptor(stackTraceElement, frame)
override fun createFrame(debugProcess: DebugProcessImpl): CapturedStackFrame {
return if (first)
CreationCoroutineStackFrame(debugProcess, this)
else
super.createFrame(debugProcess)
}
}
/**
* Restored frame in SUSPENDED coroutine, not attached to any thread.
*/
class SuspendCoroutineStackFrameItem(
val stackTraceElement: StackTraceElement,
location: Location,
spilledVariables: List<XNamedValue> = emptyList()
) : CoroutineStackFrameItem(location, spilledVariables) {
fun descriptor(frame: StackFrameProxyImpl) =
RestoredStackFrameDescriptor(stackTraceElement, frame)
}
/**
* Restored frame in RUNNING coroutine, attached to running thread. Frame references a 'preflight' or 'exit' frame.
*/
class RestoredCoroutineStackFrameItem(
val frame: StackFrameProxyImpl,
location: Location,
spilledVariables: List<XNamedValue>
) : CoroutineStackFrameItem(location, spilledVariables) {
fun descriptor() =
StackFrameDescriptorImpl(frame, MethodsTracker())
}
/**
* Restored from memory dump
*/
class DefaultCoroutineStackFrameItem(location: Location, spilledVariables: List<XNamedValue>) :
CoroutineStackFrameItem(location, spilledVariables) {
fun descriptor(frame: StackFrameProxyImpl) =
StackFrameDescriptorImpl(frame, MethodsTracker())
}
/**
* Original frame appeared before resumeWith call.
*
* Sequence is the following
*
* - KotlinStackFrame
* - invokeSuspend(KotlinStackFrame) -|
* | replaced with CoroutinePreflightStackFrame
* - resumeWith(KotlinStackFrame) ----|
* - Kotlin/JavaStackFrame -> PreCoroutineStackFrameItem : CoroutinePreflightStackFrame.threadPreCoroutineFrames
*
*/
class RunningCoroutineStackFrameItem(
val frame: StackFrameProxyImpl,
location: Location,
spilledVariables: List<XNamedValue> = emptyList()
) : CoroutineStackFrameItem(location, spilledVariables) {
override fun createFrame(debugProcess: DebugProcessImpl): CapturedStackFrame {
val realStackFrame = debugProcess.invokeInManagerThread {
debugProcess.positionManager.createStackFrame(frame, debugProcess, location)
}
return CoroutineStackFrame(debugProcess, this, realStackFrame)
}
}
sealed class CoroutineStackFrameItem(val location: Location, val spilledVariables: List<XNamedValue>) :
StackFrameItem(location, spilledVariables) {
val log by logger
override fun createFrame(debugProcess: DebugProcessImpl): CapturedStackFrame =
CoroutineStackFrame(debugProcess, this)
fun uniqueId(): String {
return location.safeSourceName() + ":" + location.safeMethod().toString() + ":" +
location.safeLineNumber() + ":" + location.safeKotlinPreferredLineNumber()
}
fun isInvokeSuspend(): Boolean =
location.isInvokeSuspend()
}
class RestoredStackFrameDescriptor(val frame: StackTraceElement, proxy: StackFrameProxyImpl) :
StackFrameDescriptorImpl(proxy, MethodsTracker())
@@ -35,14 +35,6 @@ data class CoroutineInfoData(
fun topFrameVariables() = topRestoredFrame()?.spilledVariables ?: emptyList()
fun restoredStackTrace(mode: SuspendExitMode): List<CoroutineStackFrameItem> =
if (stackTrace.isNotEmpty() && stackTrace.first().isInvokeSuspend())
stackTrace.drop(1)
else if (mode == SuspendExitMode.SUSPEND_METHOD_PARAMETER)
stackTrace.drop(1)
else
stackTrace
companion object {
val log by logger
const val DEFAULT_COROUTINE_NAME = "coroutine"
@@ -0,0 +1,16 @@
/*
* 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.data;
enum class SuspendExitMode {
SUSPEND_LAMBDA, SUSPEND_METHOD_PARAMETER, SUSPEND_METHOD, UNKNOWN, NONE;
fun isCoroutineFound() =
this == SUSPEND_LAMBDA || this == SUSPEND_METHOD_PARAMETER
fun isSuspendMethodParameter() =
this == SUSPEND_METHOD_PARAMETER
}
@@ -1,46 +0,0 @@
/*
* Copyright 2010-2019 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.data
import com.intellij.debugger.engine.JavaStackFrame
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
import com.intellij.xdebugger.XSourcePosition
import com.intellij.xdebugger.frame.XCompositeNode
import com.intellij.xdebugger.frame.XNamedValue
import com.intellij.xdebugger.frame.XValueChildrenList
/**
* Puts the frameProxy into JavaStackFrame just to instantiate. SyntheticStackFrame provides it's own data for variables view.
*/
class SyntheticStackFrame(
descriptor: StackFrameDescriptorImpl,
private val vars: List<XNamedValue>,
private val position: XSourcePosition
) :
JavaStackFrame(descriptor, true) {
override fun computeChildren(node: XCompositeNode) {
val list = XValueChildrenList()
vars.forEach { list.add(it) }
node.addChildren(list, true)
}
override fun getSourcePosition(): XSourcePosition? {
return position
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
val frame = other as? JavaStackFrame ?: return false
return descriptor.frameProxy == frame.descriptor.frameProxy
}
override fun hashCode(): Int {
return descriptor.frameProxy.hashCode()
}
}
@@ -1,114 +0,0 @@
/*
* Copyright 2010-2019 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.data
import com.intellij.debugger.engine.evaluation.EvaluateException
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.impl.DebuggerUtilsEx
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.memory.utils.StackFrameItem
import com.intellij.debugger.ui.impl.watch.MessageDescriptor
import com.intellij.debugger.ui.impl.watch.MethodsTracker
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
import com.intellij.debugger.ui.tree.render.DescriptorLabelListener
import com.intellij.icons.AllIcons
import com.sun.jdi.ObjectReference
import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBundle
import javax.swing.Icon
@Deprecated("moved to XCoroutineView")
class CoroutineDescriptorImpl(private val infoData: CoroutineInfoData) : NodeDescriptorImpl() {
lateinit var icon: Icon
override fun getName() = infoData.key.name
@Throws(EvaluateException::class)
override fun calcRepresentation(context: EvaluationContextImpl?, labelListener: DescriptorLabelListener): String {
val thread = infoData.activeThread
val name = thread?.name()?.substringBefore(" @${infoData.key.name}") ?: ""
val threadState = if (thread != null) DebuggerUtilsEx.getThreadStatusText(thread.status()) else ""
val threadName = if (name.isNotEmpty()) " on thread \"$name\":$threadState" else ""
return "${infoData.key.name}: ${infoData.key.state} $threadName"
}
override fun isExpandable() = infoData.key.state != State.CREATED
private fun calcIcon() = when {
infoData.isSuspended() -> AllIcons.Debugger.ThreadSuspended
infoData.isCreated() -> AllIcons.Debugger.ThreadStates.Idle
else -> AllIcons.Debugger.ThreadRunning
}
override fun setContext(context: EvaluationContextImpl?) {
icon = calcIcon()
}
}
@Deprecated("moved to XCoroutineView")
class CreationFramesDescriptor(val frames: List<StackTraceElement>) :
MessageDescriptor(KotlinDebuggerCoroutinesBundle.message("coroutine.dump.creation.trace"), INFORMATION) {
override fun isExpandable() = true
}
/**
* Descriptor for suspend functions
*/
@Deprecated("moved to XCoroutineView")
class SuspendStackFrameDescriptor(
val infoData: CoroutineInfoData,
val frame: StackTraceElement,
proxy: StackFrameProxyImpl,
val continuation: ObjectReference
) :
CoroutineStackFrameDescriptor(proxy) {
override fun calcRepresentation(context: EvaluationContextImpl?, labelListener: DescriptorLabelListener?): String {
return with(frame) {
val pack = className.substringBeforeLast(".", "")
val packDisplay = if (pack.isNotEmpty()) "{$pack}" else ""
"$methodName:$lineNumber, ${className.substringAfterLast(".")} $packDisplay"
}
}
override fun getName(): String? = frame.methodName
}
/**
* For the case when no data inside frame is available
*/
@Deprecated("moved to XCoroutineView")
class CoroutineCreatedStackFrameDescriptor(val frame: StackTraceElement, proxy: StackFrameProxyImpl) :
CoroutineStackFrameDescriptor(proxy) {
override fun calcRepresentation(context: EvaluationContextImpl?, labelListener: DescriptorLabelListener?): String {
return with(frame) {
val pack = className.substringBeforeLast(".", "")
val packDisplay = if (pack.isNotEmpty()) "{$pack}" else ""
"$methodName:$lineNumber, ${className.substringAfterLast(".")} $packDisplay"
}
}
override fun getName() = null
}
@Deprecated("moved to XCoroutineView")
class AsyncStackFrameDescriptor(val infoData: CoroutineInfoData, val frame: StackFrameItem, proxy: StackFrameProxyImpl) :
CoroutineStackFrameDescriptor(proxy) {
override fun calcRepresentation(context: EvaluationContextImpl?, labelListener: DescriptorLabelListener?): String {
return with(frame) {
val pack = path().substringBeforeLast(".", "")
val packDisplay = if (pack.isNotEmpty()) "{$pack}" else ""
"${method()}:${line()}, ${path().substringAfterLast(".")} $packDisplay"
}
}
override fun getName() = frame.method()
}
@Deprecated("moved to XCoroutineView")
open class CoroutineStackFrameDescriptor(proxy: StackFrameProxyImpl) : StackFrameDescriptorImpl(proxy, MethodsTracker()) {
override fun isExpandable() = false
}
@@ -0,0 +1,174 @@
/*
* 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.data
import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.JVMStackFrameInfoProvider
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.memory.utils.StackFrameItem
import com.intellij.xdebugger.frame.XCompositeNode
import com.intellij.xdebugger.frame.XNamedValue
import com.intellij.xdebugger.frame.XStackFrame
import com.intellij.xdebugger.frame.XValueChildrenList
import com.sun.jdi.Location
import org.jetbrains.kotlin.idea.debugger.*
import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBundle
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.LocationStackFrameProxyImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.util.findPosition
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isFilteredInvokeSuspend
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.stackFrame.KotlinStackFrame
/**
* Creation frame of coroutine either in RUNNING or SUSPENDED state.
*/
class CreationCoroutineStackFrameItem(
val stackTraceElement: StackTraceElement,
location: Location,
val first: Boolean
) : CoroutineStackFrameItem(location, emptyList()) {
override fun createFrame(debugProcess: DebugProcessImpl): CoroutineGeneratedFrame? {
return debugProcess.invokeInManagerThread {
val frame = debugProcess.findFirstFrame() ?: return@invokeInManagerThread null
val locationFrame = LocationStackFrameProxyImpl(location, frame)
val position = location.findPosition(debugProcess.project)
CreationCoroutineStackFrame(debugProcess, this, first)
}
}
}
/**
* Restored frame in SUSPENDED coroutine, not attached to any thread.
*/
class SuspendCoroutineStackFrameItem(
val stackTraceElement: StackTraceElement,
location: Location,
spilledVariables: List<XNamedValue> = emptyList()
) : CoroutineStackFrameItem(location, spilledVariables)
/**
* Restored from memory dump
*/
class DefaultCoroutineStackFrameItem(location: Location, spilledVariables: List<XNamedValue>) :
CoroutineStackFrameItem(location, spilledVariables) {
override fun createFrame(debugProcess: DebugProcessImpl): CoroutineGeneratedFrame? {
return debugProcess.invokeInManagerThread {
val frame = debugProcess.findFirstFrame() ?: return@invokeInManagerThread null
val locationStackFrameProxyImpl = LocationStackFrameProxyImpl(location, frame)
val position = location.findPosition(debugProcess.project) ?: return@invokeInManagerThread null
CoroutineStackFrame(debugProcess, this)
}
}
}
/**
* Original frame appeared before resumeWith call.
*
* Sequence is the following
*
* - KotlinStackFrame
* - invokeSuspend(KotlinStackFrame) -|
* | replaced with CoroutinePreflightStackFrame
* - resumeWith(KotlinStackFrame) ----|
* - Kotlin/JavaStackFrame -> PreCoroutineStackFrameItem : CoroutinePreflightStackFrame.threadPreCoroutineFrames
*
*/
open class RunningCoroutineStackFrameItem(
val frame: StackFrameProxyImpl,
spilledVariables: List<XNamedValue> = emptyList()
) : CoroutineStackFrameItem(frame.location(), spilledVariables), FrameProvider {
override fun createFrame(debugProcess: DebugProcessImpl): CoroutineGeneratedFrame? {
return debugProcess.invokeInManagerThread {
CoroutineStackFrame(debugProcess, this)
}
}
override fun provideFrame(debugProcess: DebugProcessImpl): XStackFrame? =
debugProcess.invokeInManagerThread { KotlinStackFrame(frame) }
}
sealed class CoroutineStackFrameItem(val location: Location, val spilledVariables: List<XNamedValue>) :
StackFrameItem(location, spilledVariables) {
val log by logger
override fun createFrame(debugProcess: DebugProcessImpl): CoroutineGeneratedFrame? {
return debugProcess.invokeInManagerThread {
CoroutineStackFrame(debugProcess, this)
}
}
fun uniqueId() =
location.safeSourceName() + ":" + location.safeMethod().toString() + ":" +
location.safeLineNumber() + ":" + location.safeKotlinPreferredLineNumber()
}
interface FrameProvider {
fun provideFrame(debugProcess: DebugProcessImpl): XStackFrame?
}
fun DebugProcessImpl.findFirstFrame(): StackFrameProxyImpl? =
suspendManager.pausedContext.thread?.forceFrames()?.firstOrNull()
/**
* Coroutine exit frame represented by a stack frames
* invokeSuspend():-1
* resumeWith()
*
*/
class CoroutinePreflightFrame(
val coroutineInfoData: CoroutineInfoData,
private val frame: StackFrameProxyImpl,
val threadPreCoroutineFrames: List<StackFrameProxyImpl>,
val mode: SuspendExitMode
) : KotlinStackFrame(frame), JVMStackFrameInfoProvider {
override fun superBuildVariables(evaluationContext: EvaluationContextImpl, children: XValueChildrenList) {
super.superBuildVariables(evaluationContext, children)
val topRestoredFrame = coroutineInfoData.stackTrace.firstOrNull()
if (topRestoredFrame != null && topRestoredFrame.location.isFilteredInvokeSuspend()) {
val firstFrameVariables: List<XNamedValue> = topRestoredFrame.spilledVariables
children.let {
val varNames = (0 until children.size()).map { children.getName(it) }.toSet()
firstFrameVariables.forEach {
if (!varNames.contains(it.name))
children.add(it)
}
}
}
}
override fun isInLibraryContent() = false
override fun isSynthetic() = false
}
class CreationCoroutineStackFrame(debugProcess: DebugProcessImpl, item: CoroutineStackFrameItem, val first: Boolean) : CoroutineStackFrame(debugProcess, item) {
override fun getCaptionAboveOf() = KotlinDebuggerCoroutinesBundle.message("coroutine.dump.creation.trace")
override fun hasSeparatorAbove(): Boolean =
first
}
open class CoroutineStackFrame(val debugProcess: DebugProcessImpl, val item: CoroutineStackFrameItem) :
StackFrameItem.CapturedStackFrame(debugProcess, item) {
override fun computeChildren(node: XCompositeNode) {
if (item is FrameProvider)
item.provideFrame(debugProcess)?.computeChildren(node)
else
super.computeChildren(node)
}
override fun getCaptionAboveOf() = "CoroutineExit"
override fun hasSeparatorAbove(): Boolean =
false
}
typealias CoroutineGeneratedFrame = StackFrameItem.CapturedStackFrame
@@ -0,0 +1,200 @@
/*
* 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.data
import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.JavaStackFrame
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.memory.utils.StackFrameItem
import com.intellij.debugger.ui.tree.render.DescriptorLabelListener
import com.intellij.xdebugger.XSourcePosition
import com.intellij.xdebugger.frame.XCompositeNode
import com.intellij.xdebugger.frame.XNamedValue
import com.intellij.xdebugger.frame.XStackFrame
import com.intellij.xdebugger.frame.XValueChildrenList
import com.intellij.xdebugger.impl.frame.XDebuggerFramesList
import com.sun.jdi.Location
import org.jetbrains.kotlin.idea.debugger.*
import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBundle
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.LocationStackFrameProxyImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.util.findPosition
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.stackFrame.KotlinStackFrame
/**
* Creation frame of coroutine either in RUNNING or SUSPENDED state.
*/
class CreationCoroutineStackFrameItem(
val stackTraceElement: StackTraceElement,
location: Location,
val first: Boolean
) : CoroutineStackFrameItem(location, emptyList()) {
override fun createFrame(debugProcess: DebugProcessImpl): XStackFrame? {
return debugProcess.invokeInManagerThread {
val frame = debugProcess.findFirstFrame() ?: return@invokeInManagerThread null
val locationFrame = LocationStackFrameProxyImpl(location, frame)
val position = location.findPosition(debugProcess.project)
CreationCoroutineStackFrame(locationFrame, position, first)
}
}
}
/**
* Restored frame in SUSPENDED coroutine, not attached to any thread.
*/
class SuspendCoroutineStackFrameItem(
val stackTraceElement: StackTraceElement,
location: Location,
spilledVariables: List<XNamedValue> = emptyList()
) : CoroutineStackFrameItem(location, spilledVariables)
/**
* Restored from memory dump
*/
class DefaultCoroutineStackFrameItem(location: Location, spilledVariables: List<XNamedValue>) :
CoroutineStackFrameItem(location, spilledVariables) {
override fun createFrame(debugProcess: DebugProcessImpl): XStackFrame? {
return debugProcess.invokeInManagerThread {
val frame = debugProcess.findFirstFrame() ?: return@invokeInManagerThread null
val locationStackFrameProxyImpl = LocationStackFrameProxyImpl(location, frame)
val position = location.findPosition(debugProcess.project) ?: return@invokeInManagerThread null
CoroutineStackFrame(locationStackFrameProxyImpl, position, spilledVariables, false)
}
}
}
/**
* Original frame appeared before resumeWith call.
*
* Sequence is the following
*
* - KotlinStackFrame
* - invokeSuspend(KotlinStackFrame) -|
* | replaced with CoroutinePreflightStackFrame
* - resumeWith(KotlinStackFrame) ----|
* - Kotlin/JavaStackFrame -> PreCoroutineStackFrameItem : CoroutinePreflightStackFrame.threadPreCoroutineFrames
*
*/
open class RunningCoroutineStackFrameItem(
val frame: StackFrameProxyImpl,
spilledVariables: List<XNamedValue> = emptyList()
) : CoroutineStackFrameItem(frame.location(), spilledVariables) {
override fun createFrame(debugProcess: DebugProcessImpl): XStackFrame? {
return debugProcess.invokeInManagerThread {
val position = frame.location().findPosition(debugProcess.project)
CoroutineStackFrame(frame, position)
}
}
}
sealed class CoroutineStackFrameItem(val location: Location, val spilledVariables: List<XNamedValue>) :
StackFrameItem(location, spilledVariables) {
val log by logger
override fun createFrame(debugProcess: DebugProcessImpl): XStackFrame? {
return debugProcess.invokeInManagerThread {
val frame = debugProcess.findFirstFrame() ?: return@invokeInManagerThread null
val locationFrame = LocationStackFrameProxyImpl(location, frame)
val position = location.findPosition(debugProcess.project)
CoroutineStackFrame(locationFrame, position)
}
}
fun uniqueId() =
location.safeSourceName() + ":" + location.safeMethod().toString() + ":" +
location.safeLineNumber() + ":" + location.safeKotlinPreferredLineNumber()
}
fun DebugProcessImpl.findFirstFrame(): StackFrameProxyImpl? =
suspendManager.pausedContext.thread?.forceFrames()?.firstOrNull()
/**
* Coroutine exit frame represented by a stack frames
* invokeSuspend():-1
* resumeWith()
*
*/
class CoroutinePreflightFrame(
val coroutineInfoData: CoroutineInfoData,
val frame: StackFrameProxyImpl,
val threadPreCoroutineFrames: List<StackFrameProxyImpl>,
val mode: SuspendExitMode,
private val firstFrameVariables: List<XNamedValue> = coroutineInfoData.topFrameVariables()
) : CoroutineStackFrame(frame, null, firstFrameVariables) {
override fun isInLibraryContent() = false
override fun isSynthetic() = false
}
class CreationCoroutineStackFrame(
frame: StackFrameProxyImpl,
sourcePosition: XSourcePosition?,
val first: Boolean
) : CoroutineStackFrame(frame, sourcePosition, emptyList(), false), XDebuggerFramesList.ItemWithSeparatorAbove {
override fun getCaptionAboveOf() =
KotlinDebuggerCoroutinesBundle.message("coroutine.dump.creation.trace")
override fun hasSeparatorAbove() =
first
}
open class CoroutineStackFrame(
frame: StackFrameProxyImpl,
val position: XSourcePosition?,
private val spilledVariables: List<XNamedValue>? = null,
private val includeFrameVariables: Boolean = true,
) : KotlinStackFrame(frame) {
init {
descriptor.updateRepresentation(null, DescriptorLabelListener.DUMMY_LISTENER)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
val frame = other as? JavaStackFrame ?: return false
return descriptor.frameProxy == frame.descriptor.frameProxy
}
override fun hashCode(): Int {
return descriptor.frameProxy.hashCode()
}
override fun computeChildren(node: XCompositeNode) {
if (includeFrameVariables || spilledVariables == null) {
super.computeChildren(node)
} else {
// ignore original frame variables
val list = XValueChildrenList()
spilledVariables.forEach { list.add(it) }
node.addChildren(list, true)
}
}
override fun superBuildVariables(evaluationContext: EvaluationContextImpl, children: XValueChildrenList) {
super.superBuildVariables(evaluationContext, children)
if (spilledVariables != null) {
children.let {
val varNames = (0 until children.size()).map { children.getName(it) }.toSet()
spilledVariables.forEach {
if (!varNames.contains(it.name))
children.add(it)
}
}
}
}
override fun getSourcePosition() =
position ?: super.getSourcePosition()
}
@@ -1,86 +0,0 @@
/*
* 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.data
import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.JVMStackFrameInfoProvider
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.memory.utils.StackFrameItem
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
import com.intellij.xdebugger.frame.XCompositeNode
import com.intellij.xdebugger.frame.XNamedValue
import com.intellij.xdebugger.frame.XStackFrame
import com.intellij.xdebugger.frame.XValueChildrenList
import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBundle
import org.jetbrains.kotlin.idea.debugger.stackFrame.KotlinStackFrame
/**
* Coroutine exit frame represented by a stack frames
* invokeSuspend():-1
* resumeWith()
*
*/
class CoroutinePreflightStackFrame(
val coroutineInfoData: CoroutineInfoData,
private val stackFrameDescriptorImpl: StackFrameDescriptorImpl,
val threadPreCoroutineFrames: List<StackFrameProxyImpl>,
val mode: SuspendExitMode,
private val firstFrameVariables: List<XNamedValue> = coroutineInfoData.topFrameVariables()
) : KotlinStackFrame(stackFrameDescriptorImpl), JVMStackFrameInfoProvider {
override fun superBuildVariables(evaluationContext: EvaluationContextImpl, children: XValueChildrenList) {
super.superBuildVariables(evaluationContext, children)
children.let {
val varNames = (0 until children.size()).map { children.getName(it) }.toSet()
firstFrameVariables.forEach {
if (!varNames.contains(it.name))
children.add(it)
}
}
}
override fun isInLibraryContent() = false
override fun isSynthetic() = false
fun restoredStackTrace() =
coroutineInfoData.restoredStackTrace(mode)
}
enum class SuspendExitMode {
SUSPEND_LAMBDA, SUSPEND_METHOD_PARAMETER, SUSPEND_METHOD, UNKNOWN, NONE;
fun isCoroutineFound() =
this == SUSPEND_LAMBDA || this == SUSPEND_METHOD_PARAMETER
fun isSuspendMethodParameter() =
this == SUSPEND_METHOD_PARAMETER
}
class CreationCoroutineStackFrame(debugProcess: DebugProcessImpl, item: StackFrameItem) : CoroutineStackFrame(debugProcess, item) {
override fun getCaptionAboveOf() = KotlinDebuggerCoroutinesBundle.message("coroutine.dump.creation.trace")
override fun hasSeparatorAbove(): Boolean =
true
}
open class CoroutineStackFrame(debugProcess: DebugProcessImpl, val item: StackFrameItem, private val realStackFrame: XStackFrame? = null) :
StackFrameItem.CapturedStackFrame(debugProcess, item) {
override fun computeChildren(node: XCompositeNode) {
if (realStackFrame != null)
realStackFrame.computeChildren(node)
else
super.computeChildren(node)
}
override fun getCaptionAboveOf() = "CoroutineExit"
override fun hasSeparatorAbove(): Boolean =
false
}
@@ -49,8 +49,5 @@ class ManagerThreadExecutor(val debugProcess: DebugProcessImpl) {
}
}
class ApplicationThreadExecutor {
fun schedule(f: () -> Unit, component: Component) {
return ApplicationManager.getApplication().invokeLater({ f() }, ModalityState.stateForComponent(component))
}
}
fun invokeLater(component: Component, f: () -> Unit) =
ApplicationManager.getApplication().invokeLater({ f() }, ModalityState.stateForComponent(component))
@@ -6,6 +6,14 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.sun.jdi.Location
class SkipCoroutineStackFrameProxyImpl(frame: StackFrameProxyImpl) :
StackFrameProxyImpl(frame.threadProxy(), frame.stackFrame, frame.indexFromBottom)
class LocationStackFrameProxyImpl(val location: Location, frame: StackFrameProxyImpl) :
StackFrameProxyImpl(frame.threadProxy(), frame.stackFrame, frame.indexFromBottom) {
override fun location(): Location =
location
}
@@ -8,8 +8,7 @@ package org.jetbrains.kotlin.idea.debugger.coroutine.util
import com.intellij.debugger.engine.SuspendContextImpl
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
import com.intellij.debugger.ui.impl.watch.MethodsTracker
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
import com.intellij.xdebugger.frame.XNamedValue
import com.sun.jdi.ObjectReference
import org.jetbrains.kotlin.idea.debugger.coroutine.data.*
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ContinuationHolder
@@ -23,14 +22,14 @@ class CoroutineFrameBuilder {
val log by logger
private const val PRE_FETCH_FRAME_COUNT = 5
fun build(coroutine: CoroutineInfoData, suspendContext: SuspendContextImpl): DoubleFrameList? =
fun build(coroutine: CoroutineInfoData, suspendContext: SuspendContextImpl): CoroutineFrameItemLists? =
when {
coroutine.isRunning() -> buildStackFrameForActive(coroutine, suspendContext)
coroutine.isSuspended() -> DoubleFrameList(coroutine.stackTrace, coroutine.creationStackTrace)
coroutine.isSuspended() -> CoroutineFrameItemLists(coroutine.stackTrace, coroutine.creationStackTrace)
else -> null
}
private fun buildStackFrameForActive(coroutine: CoroutineInfoData, suspendContext: SuspendContextImpl): DoubleFrameList? {
private fun buildStackFrameForActive(coroutine: CoroutineInfoData, suspendContext: SuspendContextImpl): CoroutineFrameItemLists? {
val activeThread = coroutine.activeThread ?: return null
val coroutineStackFrameList = mutableListOf<CoroutineStackFrameItem>()
@@ -43,47 +42,77 @@ class CoroutineFrameBuilder {
coroutineStackFrameList.add(it)
}
val doubleFrameList = build(preflightStackFrame, suspendContext)
coroutineStackFrameList.addAll(doubleFrameList.stackTrace)
return DoubleFrameList(coroutineStackFrameList, doubleFrameList.creationStackTrace)
val coroutineFrameLists = build(preflightStackFrame, suspendContext)
coroutineStackFrameList.addAll(coroutineFrameLists.frames)
return CoroutineFrameItemLists(coroutineStackFrameList, coroutineFrameLists.creationFrames)
} else {
buildRealStackFrameItem(runningStackFrameProxy)?.let {
coroutineStackFrameList.add(it)
}
}
}
return DoubleFrameList(coroutineStackFrameList, emptyList())
return CoroutineFrameItemLists(coroutineStackFrameList, emptyList())
}
/**
* Used by CoroutineAsyncStackTraceProvider to build XFramesView
*/
fun build(preflightFrame: CoroutinePreflightStackFrame, suspendContext: SuspendContextImpl): DoubleFrameList {
fun build(preflightFrame: CoroutinePreflightFrame, suspendContext: SuspendContextImpl): CoroutineFrameItemLists {
val stackFrames = mutableListOf<CoroutineStackFrameItem>()
stackFrames.addAll(preflightFrame.restoredStackTrace())
val (restoredStackTrace, variablesRemovedFromBottomRestoredFrame) = restoredStackTrace(
preflightFrame
)
stackFrames.addAll(restoredStackTrace)
// rest of the stack
// @TODO perhaps we need to merge the dropped frame below with the last restored (at least variables).
val framesLeft = preflightFrame.threadPreCoroutineFrames.drop(1)
// @TODO perhaps we need to merge the dropped variables with the frame below...
val framesLeft = preflightFrame.threadPreCoroutineFrames
stackFrames.addAll(framesLeft.mapIndexedNotNull { index, stackFrameProxyImpl ->
suspendContext.invokeInManagerThread { buildRealStackFrameItem(stackFrameProxyImpl) }
})
return DoubleFrameList(stackFrames, preflightFrame.coroutineInfoData.creationStackTrace)
return CoroutineFrameItemLists(stackFrames, preflightFrame.coroutineInfoData.creationStackTrace)
}
data class DoubleFrameList(
val stackTrace: List<CoroutineStackFrameItem>,
val creationStackTrace: List<CreationCoroutineStackFrameItem>
)
fun restoredStackTrace(preflightFrame: CoroutinePreflightFrame): Pair<List<CoroutineStackFrameItem>, List<XNamedValue>> {
val preflightFrameLocation = preflightFrame.stackFrameProxy.location()
val coroutineStackFrame = preflightFrame.coroutineInfoData.stackTrace
val preCoroutineTopFrameLocation = preflightFrame.threadPreCoroutineFrames.firstOrNull()?.location()
val variablesRemovedFromTopRestoredFrame = mutableListOf<XNamedValue>()
val stripTopStackTrace = coroutineStackFrame.dropWhile {
it.location.isFilterFromTop(preflightFrameLocation).apply {
if (this)
variablesRemovedFromTopRestoredFrame.addAll(it.spilledVariables)
}
}
// @TODO Need to merge variablesRemovedFromTopRestoredFrame into stripTopStackTrace.firstOrNull().spilledVariables
val variablesRemovedFromBottomRestoredFrame = mutableListOf<XNamedValue>()
val restoredFrames = when (preCoroutineTopFrameLocation) {
null -> stripTopStackTrace
else ->
stripTopStackTrace.dropLastWhile {
it.location.isFilterFromBottom(preCoroutineTopFrameLocation)
.apply { variablesRemovedFromBottomRestoredFrame.addAll(it.spilledVariables) }
}
}
return Pair(restoredFrames, variablesRemovedFromBottomRestoredFrame)
}
data class CoroutineFrameItemLists(
val frames: List<CoroutineStackFrameItem>,
val creationFrames: List<CreationCoroutineStackFrameItem>
) {
fun allFrames() =
frames + creationFrames
}
private fun buildRealStackFrameItem(
frame: StackFrameProxyImpl
): RunningCoroutineStackFrameItem? {
val location = frame.location()
val location = frame.location() ?: return null
return if (!location.safeCoroutineExitPointLineNumber())
RunningCoroutineStackFrameItem(SkipCoroutineStackFrameProxyImpl(frame), location)
RunningCoroutineStackFrameItem(SkipCoroutineStackFrameProxyImpl(frame))
else
null
}
@@ -94,14 +123,14 @@ class CoroutineFrameBuilder {
fun coroutineExitFrame(
frame: StackFrameProxyImpl,
suspendContext: SuspendContextImpl
): CoroutinePreflightStackFrame? {
): CoroutinePreflightFrame? {
return suspendContext.invokeInManagerThread {
val sem = frame.location().isPreFlight()
if (sem.isCoroutineFound()) {
val preflightStackFrame = if (sem.isCoroutineFound()) {
lookupContinuation(suspendContext, frame, sem)
} else
null
preflightStackFrame
}
}
@@ -109,7 +138,7 @@ class CoroutineFrameBuilder {
suspendContext: SuspendContextImpl,
frame: StackFrameProxyImpl,
mode: SuspendExitMode
): CoroutinePreflightStackFrame? {
): CoroutinePreflightFrame? {
if (!mode.isCoroutineFound())
return null
@@ -133,10 +162,9 @@ class CoroutineFrameBuilder {
val continuationHolder = ContinuationHolder.instance(context)
val coroutineInfo = continuationHolder.extractCoroutineInfoData(continuation) ?: return null
val descriptor = StackFrameDescriptorImpl(frame, MethodsTracker())
return CoroutinePreflightStackFrame(
return CoroutinePreflightFrame(
coroutineInfo,
descriptor,
frame,
theFollowingFrames,
mode
)
@@ -174,4 +202,4 @@ class CoroutineFrameBuilder {
return null
}
}
}
}
@@ -19,6 +19,7 @@ import com.intellij.xdebugger.XSourcePosition
import com.sun.jdi.*
import org.jetbrains.kotlin.idea.debugger.*
import org.jetbrains.kotlin.idea.debugger.coroutine.data.SuspendExitMode
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.invokeLater
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
import org.jetbrains.kotlin.idea.util.application.isUnitTestMode
@@ -71,6 +72,12 @@ fun ReferenceType.isSuspendLambda() =
fun Location.isInvokeSuspend() =
safeMethod()?.isInvokeSuspend() ?: false
fun Location.isInvokeSuspendWithNegativeLineNumber() =
isInvokeSuspend() && safeLineNumber() < 0
fun Location.isFilteredInvokeSuspend() =
isInvokeSuspend() || isInvokeSuspendWithNegativeLineNumber()
fun StackFrameProxyImpl.variableValue(variableName: String): ObjectReference? {
val continuationVariable = safeVisibleVariableByName(variableName) ?: return null
return getValue(continuationVariable) as? ObjectReference ?: return null
@@ -104,7 +111,9 @@ fun StackTraceElement.findPosition(project: Project): XSourcePosition? =
getPosition(project, className, lineNumber)
fun Location.findPosition(project: Project) =
getPosition(project, declaringType().name(), lineNumber())
readAction {
getPosition(project, declaringType().name(), lineNumber())
}
private fun getPosition(project: Project, className: String, lineNumber: Int): XSourcePosition? {
val psiFacade = JavaPsiFacade.getInstance(project)
@@ -137,3 +146,13 @@ fun threadAndContextSupportsEvaluation(suspendContext: SuspendContextImpl, frame
suspendContext.invokeInManagerThread {
suspendContext.supportsEvaluation() && frameProxy?.threadProxy()?.supportsEvaluation() ?: false
} ?: false
fun Location.sameLineAndMethod(location: Location?): Boolean =
location != null && location.safeMethod() == safeMethod() && location.safeLineNumber() == safeLineNumber()
fun Location.isFilterFromTop(location: Location?): Boolean =
isFilteredInvokeSuspend() || sameLineAndMethod(location) || location?.safeMethod() == safeMethod()
fun Location.isFilterFromBottom(location: Location?): Boolean =
sameLineAndMethod(location)
@@ -9,13 +9,7 @@ import com.intellij.debugger.engine.JavaStackFrame
import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.memory.utils.StackFrameItem
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.xdebugger.XDebuggerUtil
import com.intellij.xdebugger.XSourcePosition
import com.sun.jdi.Location
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ApplicationThreadExecutor
import org.jetbrains.kotlin.idea.debugger.safeLineNumber
import org.jetbrains.kotlin.idea.debugger.safeLocation
import org.jetbrains.kotlin.idea.debugger.safeMethod
@@ -205,10 +205,10 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
managerThreadExecutor.on(suspendContext).invoke {
val children = XValueChildrenList()
val doubleFrameList = CoroutineFrameBuilder.build(infoData, suspendContext)
doubleFrameList?.stackTrace?.forEach {
doubleFrameList?.frames?.forEach {
children.add(CoroutineFrameValue(infoData, it))
}
doubleFrameList?.creationStackTrace?.let {
doubleFrameList?.creationFrames?.let {
children.add(CreationFramesContainer(infoData, it))
}
node.addChildren(children, true)
@@ -16,9 +16,7 @@ import com.intellij.xdebugger.frame.XStackFrame
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.data.*
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ApplicationThreadExecutor
import org.jetbrains.kotlin.idea.debugger.coroutine.util.findPosition
import org.jetbrains.kotlin.idea.debugger.coroutine.util.invokeInManagerThread
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.invokeLater
import org.jetbrains.kotlin.idea.debugger.coroutine.util.suspendContextImpl
import org.jetbrains.kotlin.idea.debugger.invokeInManagerThread
import java.awt.event.KeyAdapter
@@ -26,7 +24,6 @@ import java.awt.event.KeyEvent
import java.awt.event.MouseEvent
class XDebuggerTreeSelectedNodeListener(val session: XDebugSession, val tree: XDebuggerTree) {
val applicationThreadExecutor = ApplicationThreadExecutor()
val javaDebugProcess = session.debugProcess as JavaDebugProcess
val debugProcess: DebugProcessImpl = javaDebugProcess.debuggerSession.process
@@ -54,90 +51,38 @@ class XDebuggerTreeSelectedNodeListener(val session: XDebugSession, val tree: XD
val valueContainer = node.valueContainer
val suspendContext = session.suspendContextImpl()
if (valueContainer is XCoroutineView.CoroutineFrameValue) {
when (val stackFrameItem = valueContainer.frameItem) {
is RunningCoroutineStackFrameItem -> {
val threadProxy = stackFrameItem.frame.threadProxy()
val isCurrentContext = suspendContext.thread == threadProxy
createStackAndSetFrame(threadProxy, { it.createStackFrame(stackFrameItem.frame) }, isCurrentContext)
}
is CreationCoroutineStackFrameItem -> {
val position = stackFrameItem.stackTraceElement.findPosition(session.project) ?: return false
val threadProxy = suspendContext.thread ?: return false
createStackAndSetFrame(threadProxy, {
val realFrame = threadProxy.forceFrames().first() ?: return@createStackAndSetFrame null
SyntheticStackFrame(stackFrameItem.descriptor(realFrame), emptyList(), position)
})
}
is SuspendCoroutineStackFrameItem -> {
val threadProxy = suspendContext.thread ?: return false
val position = stackFrameItem.location.findPosition(session.project)
?: return false
val frameItem = valueContainer.frameItem
val frame = frameItem.createFrame(debugProcess) ?: return false
val executionStack =
if (frameItem is RunningCoroutineStackFrameItem)
createExecutionStack(frameItem.frame.threadProxy(), debugProcess)
else
suspendContext.thread?.let {
createExecutionStack(it, debugProcess)
}
if (executionStack != null)
setCurrentStackFrame(executionStack, frame)
createStackAndSetFrame(threadProxy, {
val realFrame = threadProxy.forceFrames().first() ?: return@createStackAndSetFrame null
SyntheticStackFrame(stackFrameItem.descriptor(realFrame), stackFrameItem.spilledVariables, position)
})
}
is RestoredCoroutineStackFrameItem -> {
val threadProxy = stackFrameItem.frame.threadProxy()
val position = stackFrameItem.location.findPosition(session.project)
?: return false
createStackAndSetFrame(threadProxy, {
SyntheticStackFrame(stackFrameItem.descriptor(), stackFrameItem.spilledVariables, position)
})
}
is DefaultCoroutineStackFrameItem, is SuspendCoroutineStackFrameItem -> {
val threadProxy = suspendContext.thread ?: return false
val position = stackFrameItem.location.findPosition(session.project)
?: return false
createStackAndSetFrame(threadProxy, {
val realFrame = threadProxy.forceFrames().first() ?: return@createStackAndSetFrame null
val descriptor = when (stackFrameItem) {
is DefaultCoroutineStackFrameItem -> stackFrameItem.descriptor(realFrame)
is SuspendCoroutineStackFrameItem -> stackFrameItem.descriptor(realFrame)
else -> null
} ?: return@createStackAndSetFrame null
SyntheticStackFrame(descriptor, stackFrameItem.spilledVariables, position)
})
}
else -> {
}
}
}
}
return false
}
fun createStackAndSetFrame(
threadReferenceProxy: ThreadReferenceProxyImpl,
stackFrameProvider: (executionStack: JavaExecutionStack) -> XStackFrame?,
isCurrentContext: Boolean = false
) {
val stackFrameStack = debugProcess.invokeInManagerThread {
val executionStack = createExecutionStack(threadReferenceProxy, isCurrentContext);
val stackFrame = stackFrameProvider.invoke(executionStack) ?: return@invokeInManagerThread null
XStackFrameStack(stackFrame, executionStack)
} ?: return
setCurrentStackFrame(stackFrameStack)
}
fun setCurrentStackFrame(stackFrameStack: XStackFrameStack) {
applicationThreadExecutor.schedule(
{
session.setCurrentStackFrame(stackFrameStack.executionStack, stackFrameStack.stackFrame, false)
}, tree
)
}
data class XStackFrameStack(val stackFrame: XStackFrame, val executionStack: XExecutionStack)
private fun createExecutionStack(proxy: ThreadReferenceProxyImpl, isCurrentContext: Boolean = false): JavaExecutionStack {
val executionStack = JavaExecutionStack(proxy, debugProcess, isCurrentContext)
executionStack.initTopFrame()
return executionStack
}
fun setCurrentStackFrame(executionStack: XExecutionStack, stackFrame: XStackFrame) =
invokeLater(tree) {
session.setCurrentStackFrame(executionStack, stackFrame, false)
}
}
fun createExecutionStack(threadReference: ThreadReferenceProxyImpl, debugProcess: DebugProcessImpl): JavaExecutionStack? =
debugProcess.invokeInManagerThread {
val executionStack = JavaExecutionStack(threadReference, debugProcess, debugProcess.isSameContext(threadReference))
executionStack.initTopFrame()
executionStack
}
data class KeyMouseEvent(val keyEvent: KeyEvent?, val mouseEvent: MouseEvent?) {
constructor(keyEvent: KeyEvent) : this(keyEvent, null)
constructor(mouseEvent: MouseEvent) : this(null, mouseEvent)
@@ -145,4 +90,7 @@ data class KeyMouseEvent(val keyEvent: KeyEvent?, val mouseEvent: MouseEvent?) {
fun isKeyEvent() = keyEvent != null
fun isMouseEvent() = mouseEvent != null
}
}
fun DebugProcessImpl.isSameContext(threadReference: ThreadReferenceProxyImpl): Boolean =
suspendManager.pausedContext.thread == threadReference