(CoroutineDebugger) Minor issued fixed, log level adjusted for missing classes in JVM.

This commit is contained in:
Vladimir Ilmov
2020-04-22 13:56:36 +02:00
parent eec3263518
commit 9b0e37f627
31 changed files with 191 additions and 447 deletions
@@ -4,7 +4,6 @@
*/ */
package org.jetbrains.kotlin.idea.debugger.coroutine; package org.jetbrains.kotlin.idea.debugger.coroutine;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.RunConfigurationExtension; import com.intellij.execution.RunConfigurationExtension;
import com.intellij.execution.configurations.JavaParameters; import com.intellij.execution.configurations.JavaParameters;
import com.intellij.execution.configurations.RunConfigurationBase; import com.intellij.execution.configurations.RunConfigurationBase;
@@ -19,7 +18,7 @@ public class CoroutineDebugConfigurationExtension extends RunConfigurationExtens
@Override @Override
public <T extends RunConfigurationBase> void updateJavaParameters( public <T extends RunConfigurationBase> void updateJavaParameters(
@NotNull T configuration, @NotNull JavaParameters params, RunnerSettings runnerSettings T configuration, @NotNull JavaParameters params, RunnerSettings runnerSettings
) { ) {
if (configuration != null) { if (configuration != null) {
Project project = configuration.getProject(); Project project = configuration.getProject();
@@ -5,11 +5,9 @@
package org.jetbrains.kotlin.idea.debugger.coroutine package org.jetbrains.kotlin.idea.debugger.coroutine
import org.jetbrains.annotations.NonNls
class CoroutineDebuggerContentInfo { class CoroutineDebuggerContentInfo {
companion object { companion object {
val XCOROUTINE_THREADS_CONTENT = "XCoroutineThreadsContent" const val XCOROUTINE_THREADS_CONTENT = "XCoroutineThreadsContent"
val XCOROUTINE_POPUP_ACTION_GROUP = "Kotlin.XDebugger.Actions" const val XCOROUTINE_POPUP_ACTION_GROUP = "Kotlin.XDebugger.Actions"
} }
} }
@@ -7,16 +7,12 @@ package org.jetbrains.kotlin.idea.debugger.coroutine
import com.intellij.debugger.actions.AsyncStacksToggleAction import com.intellij.debugger.actions.AsyncStacksToggleAction
import com.intellij.debugger.engine.DebugProcessImpl import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.SuspendContextImpl
import com.intellij.debugger.jdi.StackFrameProxyImpl import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.openapi.application.Application
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.xdebugger.frame.XStackFrame import com.intellij.xdebugger.frame.XStackFrame
import com.intellij.xdebugger.impl.XDebugSessionImpl import com.intellij.xdebugger.impl.XDebugSessionImpl
import com.sun.jdi.Location import com.sun.jdi.Location
import org.jetbrains.kotlin.idea.debugger.StackFrameInterceptor import org.jetbrains.kotlin.idea.debugger.StackFrameInterceptor
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ContinuationHolder
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.SkipCoroutineStackFrameProxyImpl import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.SkipCoroutineStackFrameProxyImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CoroutineFrameBuilder import org.jetbrains.kotlin.idea.debugger.coroutine.util.CoroutineFrameBuilder
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isInUnitTest import org.jetbrains.kotlin.idea.debugger.coroutine.util.isInUnitTest
@@ -45,12 +45,15 @@ class DebuggerConnection(
val kotlinxCoroutinesCore = params.classPath?.pathList?.firstOrNull { it.contains("kotlinx-coroutines-core") } val kotlinxCoroutinesCore = params.classPath?.pathList?.firstOrNull { it.contains("kotlinx-coroutines-core") }
val kotlinxCoroutinesDebug = params.classPath?.pathList?.firstOrNull { it.contains("kotlinx-coroutines-debug") } val kotlinxCoroutinesDebug = params.classPath?.pathList?.firstOrNull { it.contains("kotlinx-coroutines-debug") }
val mode = if (kotlinxCoroutinesDebug != null) { val mode = when {
CoroutineDebuggerMode.VERSION_UP_TO_1_3_5 kotlinxCoroutinesDebug != null -> {
} else if (kotlinxCoroutinesCore != null) { CoroutineDebuggerMode.VERSION_UP_TO_1_3_5
determineCoreVersionMode(kotlinxCoroutinesCore) }
} else kotlinxCoroutinesCore != null -> {
CoroutineDebuggerMode.DISABLED determineCoreVersionMode(kotlinxCoroutinesCore)
}
else -> CoroutineDebuggerMode.DISABLED
}
when (mode) { when (mode) {
CoroutineDebuggerMode.VERSION_1_3_6_AND_UP -> initializeCoroutineAgent(params, kotlinxCoroutinesCore) CoroutineDebuggerMode.VERSION_1_3_6_AND_UP -> initializeCoroutineAgent(params, kotlinxCoroutinesCore)
@@ -62,12 +65,12 @@ class DebuggerConnection(
} }
private fun determineCoreVersionMode(kotlinxCoroutinesCore: String): CoroutineDebuggerMode { private fun determineCoreVersionMode(kotlinxCoroutinesCore: String): CoroutineDebuggerMode {
val regex = Regex(""".+\Wkotlinx\-coroutines\-core\-(.+)?\.jar""") val regex = Regex(""".+\Wkotlinx-coroutines-core-(.+)?\.jar""")
val matchResult = regex.matchEntire(kotlinxCoroutinesCore) ?: return CoroutineDebuggerMode.DISABLED val matchResult = regex.matchEntire(kotlinxCoroutinesCore) ?: return CoroutineDebuggerMode.DISABLED
val coroutinesCoreVersion = DefaultArtifactVersion(matchResult.groupValues.get(1)) val coroutinesCoreVersion = DefaultArtifactVersion(matchResult.groupValues[1])
val versionToCompareTo = DefaultArtifactVersion("1.3.5") val versionToCompareTo = DefaultArtifactVersion("1.3.5")
return if (versionToCompareTo.compareTo(coroutinesCoreVersion) < 0) return if (versionToCompareTo < coroutinesCoreVersion)
CoroutineDebuggerMode.VERSION_1_3_6_AND_UP CoroutineDebuggerMode.VERSION_1_3_6_AND_UP
else else
CoroutineDebuggerMode.DISABLED CoroutineDebuggerMode.DISABLED
@@ -24,9 +24,9 @@ import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.text.DateFormatUtil import com.intellij.util.text.DateFormatUtil
import com.intellij.xdebugger.impl.XDebuggerManagerImpl import com.intellij.xdebugger.impl.XDebuggerManagerImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBundle import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBundle
import org.jetbrains.kotlin.idea.debugger.coroutine.view.CoroutineDumpPanel
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.proxy.CoroutineDebugProbesProxy import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.CoroutineDebugProbesProxy
import org.jetbrains.kotlin.idea.debugger.coroutine.view.CoroutineDumpPanel
@Suppress("ComponentNotRegistered") @Suppress("ComponentNotRegistered")
class CoroutineDumpAction : AnAction(), AnAction.TransparentUpdate { class CoroutineDumpAction : AnAction(), AnAction.TransparentUpdate {
@@ -49,7 +49,7 @@ class CoroutineDumpAction : AnAction(), AnAction.TransparentUpdate {
ApplicationManager.getApplication().invokeLater(f, ModalityState.NON_MODAL) ApplicationManager.getApplication().invokeLater(f, ModalityState.NON_MODAL)
} else { } else {
val message = KotlinDebuggerCoroutinesBundle.message("coroutine.dump.failed") val message = KotlinDebuggerCoroutinesBundle.message("coroutine.dump.failed")
XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification(message,MessageType.ERROR).notify(project) XDebuggerManagerImpl.NOTIFICATION_GROUP.createNotification(message, MessageType.ERROR).notify(project)
} }
} }
}) })
@@ -12,13 +12,12 @@ import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.sun.jdi.ObjectReference import com.sun.jdi.ObjectReference
import com.sun.jdi.Value import com.sun.jdi.Value
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ContinuationHolder
class ContinuationValueDescriptorImpl( class ContinuationValueDescriptorImpl(
project: Project, project: Project,
val continuation: ObjectReference, val continuation: ObjectReference,
val fieldName: String, val fieldName: String,
val variableName: String private val variableName: String
) : ValueDescriptorImpl(project) { ) : ValueDescriptorImpl(project) {
override fun calcValueName() = variableName override fun calcValueName() = variableName
@@ -9,7 +9,7 @@ import com.sun.jdi.ObjectReference
import com.sun.jdi.ThreadReference import com.sun.jdi.ThreadReference
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData.Companion.DEFAULT_COROUTINE_NAME import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData.Companion.DEFAULT_COROUTINE_NAME
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData.Companion.DEFAULT_COROUTINE_STATE import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData.Companion.DEFAULT_COROUTINE_STATE
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.* import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.MirrorOfCoroutineInfo
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
/** /**
@@ -31,9 +31,9 @@ data class CoroutineInfoData(
fun isRunning() = key.state == State.RUNNING fun isRunning() = key.state == State.RUNNING
fun topRestoredFrame() = stackTrace.firstOrNull() private fun topRestoredFrame() = stackTrace.firstOrNull()
fun topFrameVariables() = stackTrace.firstOrNull()?.spilledVariables ?: emptyList() fun topFrameVariables() = topRestoredFrame()?.spilledVariables ?: emptyList()
fun restoredStackTrace(mode: SuspendExitMode): List<CoroutineStackFrameItem> = fun restoredStackTrace(mode: SuspendExitMode): List<CoroutineStackFrameItem> =
if (stackTrace.isNotEmpty() && stackTrace.first().isInvokeSuspend()) if (stackTrace.isNotEmpty() && stackTrace.first().isInvokeSuspend())
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBund
import javax.swing.Icon import javax.swing.Icon
@Deprecated("moved to XCoroutineView") @Deprecated("moved to XCoroutineView")
class CoroutineDescriptorImpl(val infoData: CoroutineInfoData) : NodeDescriptorImpl() { class CoroutineDescriptorImpl(private val infoData: CoroutineInfoData) : NodeDescriptorImpl() {
lateinit var icon: Icon lateinit var icon: Icon
override fun getName() = infoData.key.name override fun getName() = infoData.key.name
@@ -74,10 +74,9 @@ class SuspendStackFrameDescriptor(
} }
} }
override fun getName() : String? = frame.methodName override fun getName(): String? = frame.methodName
} }
/** /**
* For the case when no data inside frame is available * For the case when no data inside frame is available
*/ */
@@ -10,16 +10,11 @@ import com.intellij.debugger.jdi.LocalVariableProxyImpl
import com.intellij.debugger.jdi.StackFrameProxyImpl import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.memory.utils.StackFrameItem import com.intellij.debugger.memory.utils.StackFrameItem
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
import com.intellij.ui.ColoredTextContainer
import com.intellij.ui.SimpleTextAttributes
import com.intellij.xdebugger.frame.XCompositeNode import com.intellij.xdebugger.frame.XCompositeNode
import com.intellij.xdebugger.frame.XNamedValue import com.intellij.xdebugger.frame.XNamedValue
import com.intellij.xdebugger.frame.XStackFrame import com.intellij.xdebugger.frame.XStackFrame
import com.intellij.xdebugger.frame.XValueChildrenList import com.intellij.xdebugger.frame.XValueChildrenList
import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBundle import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBundle
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.SkipCoroutineStackFrameProxyImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.util.coroutineDebuggerTraceEnabled
import org.jetbrains.kotlin.idea.debugger.invokeInManagerThread
import org.jetbrains.kotlin.idea.debugger.stackFrame.KotlinStackFrame import org.jetbrains.kotlin.idea.debugger.stackFrame.KotlinStackFrame
@@ -32,10 +27,10 @@ import org.jetbrains.kotlin.idea.debugger.stackFrame.KotlinStackFrame
class CoroutinePreflightStackFrame( class CoroutinePreflightStackFrame(
val coroutineInfoData: CoroutineInfoData, val coroutineInfoData: CoroutineInfoData,
val stackFrameDescriptorImpl: StackFrameDescriptorImpl, private val stackFrameDescriptorImpl: StackFrameDescriptorImpl,
val threadPreCoroutineFrames: List<StackFrameProxyImpl>, val threadPreCoroutineFrames: List<StackFrameProxyImpl>,
val mode: SuspendExitMode, val mode: SuspendExitMode,
val firstFrameVariables: List<XNamedValue> = coroutineInfoData.topFrameVariables() private val firstFrameVariables: List<XNamedValue> = coroutineInfoData.topFrameVariables()
) : KotlinStackFrame(stackFrameDescriptorImpl), JVMStackFrameInfoProvider { ) : KotlinStackFrame(stackFrameDescriptorImpl), JVMStackFrameInfoProvider {
override fun computeChildren(node: XCompositeNode) { override fun computeChildren(node: XCompositeNode) {
@@ -49,7 +44,7 @@ class CoroutinePreflightStackFrame(
override fun getVisibleVariables(): List<LocalVariableProxyImpl> { override fun getVisibleVariables(): List<LocalVariableProxyImpl> {
// skip restored variables // skip restored variables
return super.getVisibleVariables().filter { v -> ! firstFrameVariables.any { it.name == v.name() } } return super.getVisibleVariables().filter { v -> !firstFrameVariables.any { it.name == v.name() } }
} }
override fun isInLibraryContent() = false override fun isInLibraryContent() = false
@@ -77,7 +72,7 @@ class CreationCoroutineStackFrame(debugProcess: DebugProcessImpl, item: StackFra
true true
} }
open class CoroutineStackFrame(debugProcess: DebugProcessImpl, val item: StackFrameItem, val realStackFrame: XStackFrame? = null) : open class CoroutineStackFrame(debugProcess: DebugProcessImpl, val item: StackFrameItem, private val realStackFrame: XStackFrame? = null) :
StackFrameItem.CapturedStackFrame(debugProcess, item) { StackFrameItem.CapturedStackFrame(debugProcess, item) {
override fun computeChildren(node: XCompositeNode) { override fun computeChildren(node: XCompositeNode) {
@@ -6,10 +6,11 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy package org.jetbrains.kotlin.idea.debugger.coroutine.proxy
import com.intellij.debugger.engine.JavaValue import com.intellij.debugger.engine.JavaValue
import com.sun.jdi.* import com.sun.jdi.ObjectReference
import org.jetbrains.kotlin.idea.debugger.coroutine.data.* import org.jetbrains.kotlin.idea.debugger.coroutine.data.*
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.* import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.*
import org.jetbrains.kotlin.idea.debugger.coroutine.util.* import org.jetbrains.kotlin.idea.debugger.coroutine.util.isAbstractCoroutine
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
class ContinuationHolder private constructor(val context: DefaultExecutionContext) { class ContinuationHolder private constructor(val context: DefaultExecutionContext) {
@@ -46,8 +47,8 @@ class ContinuationHolder private constructor(val context: DefaultExecutionContex
val ci = debugProbesImpl?.getCoroutineInfo(input, context) val ci = debugProbesImpl?.getCoroutineInfo(input, context)
if (ci != null) { if (ci != null) {
if (ci.creationStackTrace != null) if (ci.creationStackTrace != null)
for (index in 0 until ci.creationStackTrace.size) { for (index in ci.creationStackTrace.indices) {
val frame = ci.creationStackTrace.get(index) val frame = ci.creationStackTrace[index]
val ste = frame.stackTraceElement() val ste = frame.stackTraceElement()
val location = locationCache.createLocation(ste) val location = locationCache.createLocation(ste)
creationStackTrace.add(CreationCoroutineStackFrameItem(ste, location, index == 0)) creationStackTrace.add(CreationCoroutineStackFrameItem(ste, location, index == 0))
@@ -64,14 +65,14 @@ class ContinuationHolder private constructor(val context: DefaultExecutionContex
fun state(value: ObjectReference?): CoroutineNameIdState? { fun state(value: ObjectReference?): CoroutineNameIdState? {
value ?: return null value ?: return null
val reference = JavaLangMirror(context) val reference = JavaLangMirror(context)
val standaloneCoroutine = StandaloneCoroutine(context) val standaloneCoroutine = StandaloneCoroutine.instance(context) ?: return null
val standAloneCoroutineMirror = standaloneCoroutine.mirror(value, context) val standAloneCoroutineMirror = standaloneCoroutine.mirror(value, context)
if (standAloneCoroutineMirror?.context is MirrorOfCoroutineContext) { if (standAloneCoroutineMirror?.context is MirrorOfCoroutineContext) {
val id = standAloneCoroutineMirror.context.id val id = standAloneCoroutineMirror.context.id
val name = standAloneCoroutineMirror.context.name ?: CoroutineInfoData.DEFAULT_COROUTINE_NAME val name = standAloneCoroutineMirror.context.name ?: CoroutineInfoData.DEFAULT_COROUTINE_NAME
val toString = reference.string(value, context) val toString = reference.string(value, context)
// trying to get coroutine information by calling JobSupport.toString(), ${nameString()}{${stateString(state)}}@$hexAddress // trying to get coroutine information by calling JobSupport.toString(), ${nameString()}{${stateString(state)}}@$hexAddress
val r = """\w+\{(\w+)\}\@([\w\d]+)""".toRegex() val r = """\w+\{(\w+)}@([\w\d]+)""".toRegex()
val matcher = r.toPattern().matcher(toString) val matcher = r.toPattern().matcher(toString)
if (matcher.matches()) { if (matcher.matches()) {
val state = stateOf(matcher.group(1)) val state = stateOf(matcher.group(1))
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.idea.debugger.coroutine.proxy
import com.intellij.debugger.engine.DebuggerManagerThreadImpl import com.intellij.debugger.engine.DebuggerManagerThreadImpl
import com.intellij.debugger.engine.SuspendContextImpl import com.intellij.debugger.engine.SuspendContextImpl
import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.registry.Registry
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CoroutineFrameBuilder
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoCache import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoCache
import org.jetbrains.kotlin.idea.debugger.coroutine.util.executionContext import org.jetbrains.kotlin.idea.debugger.coroutine.util.executionContext
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
@@ -6,7 +6,10 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy package org.jetbrains.kotlin.idea.debugger.coroutine.proxy
import com.intellij.xdebugger.frame.XNamedValue import com.intellij.xdebugger.frame.XNamedValue
import org.jetbrains.kotlin.idea.debugger.coroutine.data.* import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineInfoData
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineNameIdState
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CreationCoroutineStackFrameItem
import org.jetbrains.kotlin.idea.debugger.coroutine.data.SuspendCoroutineStackFrameItem
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.DebugMetadata import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.DebugMetadata
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.DebugProbesImpl import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.DebugProbesImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.MirrorOfCoroutineInfo import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.MirrorOfCoroutineInfo
@@ -26,16 +29,16 @@ class CoroutineLibraryAgent2Proxy(private val executionContext: DefaultExecution
return result.mapNotNull { mapToCoroutineInfoData(it) } return result.mapNotNull { mapToCoroutineInfoData(it) }
} }
fun mapToCoroutineInfoData(mirror: MirrorOfCoroutineInfo): CoroutineInfoData? { private fun mapToCoroutineInfoData(mirror: MirrorOfCoroutineInfo): CoroutineInfoData? {
val cnis = CoroutineNameIdState.instance(mirror) val coroutineNameIdState = CoroutineNameIdState.instance(mirror)
val stackTrace = mirror.enchancedStackTrace?.mapNotNull { it.stackTraceElement() } ?: emptyList() val stackTrace = mirror.enhancedStackTrace?.mapNotNull { it.stackTraceElement() } ?: emptyList()
val variables: List<XNamedValue> = mirror.lastObservedFrame?.let { val variables: List<XNamedValue> = mirror.lastObservedFrame?.let {
val spilledVariables = debugMetadata?.baseContinuationImpl?.mirror(it, executionContext) val spilledVariables = debugMetadata?.baseContinuationImpl?.mirror(it, executionContext)
spilledVariables?.spilledValues(executionContext) spilledVariables?.spilledValues(executionContext)
} ?: emptyList() } ?: emptyList()
var stackFrames = findStackFrames(stackTrace, variables) val stackFrames = findStackFrames(stackTrace, variables)
return CoroutineInfoData( return CoroutineInfoData(
cnis, coroutineNameIdState,
stackFrames.restoredStackFrames, stackFrames.restoredStackFrames,
stackFrames.creationStackFrames, stackFrames.creationStackFrames,
mirror.lastObservedThread, mirror.lastObservedThread,
@@ -44,11 +47,11 @@ class CoroutineLibraryAgent2Proxy(private val executionContext: DefaultExecution
} }
fun isInstalled(): Boolean { fun isInstalled(): Boolean {
try { return try {
return debugProbesImpl?.isInstalledValue ?: false debugProbesImpl?.isInstalledValue ?: false
} catch (e: Exception) { } catch (e: Exception) {
log.error("Exception happened while checking agent status.", e) log.error("Exception happened while checking agent status.", e)
return false false
} }
} }
@@ -74,10 +77,10 @@ class CoroutineLibraryAgent2Proxy(private val executionContext: DefaultExecution
companion object { companion object {
fun instance(executionContext: DefaultExecutionContext): CoroutineLibraryAgent2Proxy? { fun instance(executionContext: DefaultExecutionContext): CoroutineLibraryAgent2Proxy? {
val agentProxy = CoroutineLibraryAgent2Proxy(executionContext) val agentProxy = CoroutineLibraryAgent2Proxy(executionContext)
if (agentProxy.isInstalled()) return if (agentProxy.isInstalled())
return agentProxy agentProxy
else else
return null null
} }
} }
@@ -1,190 +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.proxy
import com.intellij.debugger.engine.evaluation.EvaluateException
import com.sun.jdi.*
import org.jetbrains.kotlin.idea.debugger.coroutine.data.*
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.CoroutineContext
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.JavaLangMirror
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isCreationSeparatorFrame
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
class CoroutineLibraryAgentProxy(private val debugProbesClsRef: ClassType, private val executionContext: DefaultExecutionContext) :
CoroutineInfoProvider {
private val coroutineContextReference: JavaLangMirror = JavaLangMirror(executionContext)
private val debugProbesImplClsRef = executionContext.findClass("$DEBUG_PACKAGE.internal.DebugProbesImpl") as ClassType
private val debugProbesImplInstance = with(debugProbesImplClsRef) { getValue(fieldByName("INSTANCE")) as ObjectReference }
private val enhanceStackTraceWithThreadDumpRef: Method = debugProbesImplClsRef
.methodsByName("enhanceStackTraceWithThreadDump").single()
private val dumpMethod: Method = debugProbesClsRef.concreteMethodByName("dumpCoroutinesInfo", "()Ljava/util/List;")
val instance = with(debugProbesClsRef) { getValue(fieldByName("INSTANCE")) as ObjectReference }
// CoroutineInfo
private val coroutineInfoClsRef = executionContext.findClass("$DEBUG_PACKAGE.CoroutineInfo") as ClassType
private val getStateRef: Method = coroutineInfoClsRef.concreteMethodByName("getState", "()Lkotlinx/coroutines/debug/State;")
private val getContextRef: Method = coroutineInfoClsRef.concreteMethodByName("getContext", "()Lkotlin/coroutines/CoroutineContext;")
private val lastObservedStackTraceRef: Method = coroutineInfoClsRef.methodsByName("lastObservedStackTrace").single()
private val sequenceNumberFieldRef: Field = coroutineInfoClsRef.fieldByName("sequenceNumber")
private val lastObservedThreadFieldRef: Field = coroutineInfoClsRef.fieldByName("lastObservedThread")
private val lastObservedFrameFieldRef: Field = coroutineInfoClsRef.fieldByName("lastObservedFrame") // continuation
// value
private val vm = executionContext.vm
private val locationCache = LocationCache(executionContext)
private val coroutineContext: CoroutineContext = CoroutineContext(executionContext)
@Synchronized
@Suppress("unused")
fun install() =
executionContext.invokeMethodAsVoid(instance, "install")
@Synchronized
@Suppress("unused")
fun uninstall() =
executionContext.invokeMethodAsVoid(instance, "uninstall")
override fun dumpCoroutinesInfo(): List<CoroutineInfoData> {
val coroutinesInfo = executionContext.invokeMethodAsObject(instance, dumpMethod) ?: return emptyList()
executionContext.keepReference(coroutinesInfo)
val size = coroutineContextReference.sizeOf(coroutinesInfo, executionContext)
return MutableList(size) {
val elem = coroutineContextReference.elementFromList(coroutinesInfo, it, executionContext)
fetchCoroutineState(elem)
}
}
private fun fetchCoroutineState(instance: ObjectReference): CoroutineInfoData {
val name = getName(instance)
val state = getState(instance)
val thread = getLastObservedThread(instance, lastObservedThreadFieldRef)
val lastObservedFrameFieldRef = instance.getValue(lastObservedFrameFieldRef) as? ObjectReference
val stackTrace = getStackTrace(instance)
val creationFrameSeparatorIndex = findCreationFrameIndex(stackTrace)
val coroutineStackTrace = stackTrace.take(creationFrameSeparatorIndex)
val coroutineStackTraceFrameItems = coroutineStackTrace.map {
SuspendCoroutineStackFrameItem(it, locationCache.createLocation(it))
}
val creationStackTrace = stackTrace.subList(creationFrameSeparatorIndex + 1, stackTrace.size)
val creationStackTraceFrameItems = creationStackTrace.mapIndexed { index, stackTraceElement ->
CreationCoroutineStackFrameItem(stackTraceElement, locationCache.createLocation(stackTraceElement), index == 0)
}
val key = CoroutineNameIdState(name, "", State.valueOf(state), "")
return CoroutineInfoData(
key,
coroutineStackTraceFrameItems.toMutableList(),
creationStackTraceFrameItems,
thread,
lastObservedFrameFieldRef
)
}
/**
* Tries to find creation frame separator if any, returns last index if none found
*/
private fun findCreationFrameIndex(frames: List<StackTraceElement>): Int {
val index = frames.indexOfFirst { it.isCreationSeparatorFrame() }
return if (index < 0)
frames.lastIndex
else
index
}
private fun getName(
info: ObjectReference // CoroutineInfo instance
): String {
// equals to `coroutineInfo.context.get(CoroutineName).name`
val coroutineContextInst = executionContext.invokeMethod(
info,
getContextRef,
emptyList()
) as? ObjectReference ?: throw IllegalArgumentException("Coroutine context must not be null")
val context = coroutineContext.mirror(coroutineContextInst, executionContext)
val name = context?.name ?: "coroutine"
val id = (info.getValue(sequenceNumberFieldRef) as LongValue).value()
return "$name#$id"
}
private fun getState(
info: ObjectReference // CoroutineInfo instance
): String {
// equals to `stringState = coroutineInfo.state.toString()`
val state = executionContext.invokeMethod(info, getStateRef, emptyList()) as ObjectReference
return coroutineContextReference.string(state, executionContext)
}
private fun getLastObservedThread(
info: ObjectReference, // CoroutineInfo instance
threadRef: Field // reference to lastObservedThread
): ThreadReference? = info.getValue(threadRef) as? ThreadReference
/**
* Returns list of stackTraceElements for the given CoroutineInfo's [ObjectReference]
*/
private fun getStackTrace(
info: ObjectReference
): List<StackTraceElement> {
val frameList = lastObservedStackTrace(info)
// val tmpList = mutableListOf<StackTraceElement>()
// val sizeOfFrameList = coroutineContextReference.sizeOf(frameList, executionContext)
// for (it in 0 until sizeOfFrameList) {
// val frame = coroutineContextReference.elementFromList(frameList, it, executionContext)
// val ste = coroutineContextReference.stackTraceElement(frame)
// tmpList.add(ste)
// }
val mergedFrameList = enhanceStackTraceWithThreadDump(listOf(info, frameList))
val sizeOfMergedFrameList = coroutineContextReference.sizeOf(mergedFrameList, executionContext)
val list = mutableListOf<StackTraceElement>()
for (it in 0 until sizeOfMergedFrameList) {
val frame = coroutineContextReference.elementFromList(mergedFrameList, it, executionContext)
val ste = coroutineContextReference.stackTraceElement(frame)
list.add(// 0, // add in the beginning // @TODO what's the point?
ste
)
}
return list
}
private fun lastObservedStackTrace(instance: ObjectReference) =
executionContext.invokeMethod(instance, lastObservedStackTraceRef, emptyList()) as ObjectReference
private fun enhanceStackTraceWithThreadDump(args: List<ObjectReference>) =
executionContext.invokeMethod(
debugProbesImplInstance,
enhanceStackTraceWithThreadDumpRef, args
) as ObjectReference
companion object {
private const val DEBUG_PACKAGE = "kotlinx.coroutines.debug"
fun instance(executionContext: DefaultExecutionContext): CoroutineLibraryAgentProxy? {
try {
val debugProbesClsRef = executionContext.findClass("$DEBUG_PACKAGE.DebugProbes") ?: return null
if (debugProbesClsRef is ClassType) {
val instanceField = debugProbesClsRef.fieldByName("INSTANCE")
val debugProbes = debugProbesClsRef.getValue(instanceField) as ObjectReference
val f = debugProbes.referenceType().fieldByName("isInstalled")
val debugProbesActivated = if (f != null) (debugProbes.getValue(f) as BooleanValue).value() else true
if (debugProbesActivated)
return CoroutineLibraryAgentProxy(debugProbesClsRef, executionContext)
}
} catch (e: EvaluateException) {
}
return null
}
}
}
@@ -6,7 +6,8 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy package org.jetbrains.kotlin.idea.debugger.coroutine.proxy
import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.util.registry.Registry
import com.sun.jdi.* import com.sun.jdi.Field
import com.sun.jdi.ObjectReference
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.proxy.mirror.CancellableContinuationImpl import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror.CancellableContinuationImpl
import org.jetbrains.kotlin.idea.debugger.coroutine.util.findCancellableContinuationImplReferenceType import org.jetbrains.kotlin.idea.debugger.coroutine.util.findCancellableContinuationImplReferenceType
@@ -15,10 +16,10 @@ import org.jetbrains.kotlin.idea.debugger.coroutine.util.findDispatchedContinuat
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
class CoroutineNoLibraryProxy(val executionContext: DefaultExecutionContext) : CoroutineInfoProvider { class CoroutineNoLibraryProxy(private val executionContext: DefaultExecutionContext) : CoroutineInfoProvider {
val log by logger val log by logger
val debugMetadataKtType = executionContext.findCoroutineMetadataType() private val debugMetadataKtType = executionContext.findCoroutineMetadataType()
val holder = ContinuationHolder.instance(executionContext) private val holder = ContinuationHolder.instance(executionContext)
override fun dumpCoroutinesInfo(): List<CoroutineInfoData> { override fun dumpCoroutinesInfo(): List<CoroutineInfoData> {
val vm = executionContext.vm val vm = executionContext.vm
@@ -49,13 +50,13 @@ class CoroutineNoLibraryProxy(val executionContext: DefaultExecutionContext) : C
return false return false
} }
fun extractCancellableContinuation( private fun extractCancellableContinuation(
dispatchedContinuation: ObjectReference, dispatchedContinuation: ObjectReference,
ccMirrorProvider: CancellableContinuationImpl ccMirrorProvider: CancellableContinuationImpl
): CoroutineInfoData? { ): CoroutineInfoData? {
val mirror = ccMirrorProvider.mirror(dispatchedContinuation, executionContext) ?: return null val mirror = ccMirrorProvider.mirror(dispatchedContinuation, executionContext) ?: return null
val continuation = mirror.delegate?.continuation ?: return null val continuation = mirror.delegate?.continuation ?: return null
return holder?.extractCoroutineInfoData(continuation) return holder.extractCoroutineInfoData(continuation)
} }
private fun dispatchedContinuation(resultList: MutableList<CoroutineInfoData>): Boolean { private fun dispatchedContinuation(resultList: MutableList<CoroutineInfoData>): Boolean {
@@ -75,7 +76,7 @@ class CoroutineNoLibraryProxy(val executionContext: DefaultExecutionContext) : C
private fun extractDispatchedContinuation(dispatchedContinuation: ObjectReference, continuation: Field): CoroutineInfoData? { private fun extractDispatchedContinuation(dispatchedContinuation: ObjectReference, continuation: Field): CoroutineInfoData? {
debugMetadataKtType ?: return null debugMetadataKtType ?: return null
val initialContinuation = dispatchedContinuation.getValue(continuation) as ObjectReference val initialContinuation = dispatchedContinuation.getValue(continuation) as ObjectReference
return holder?.extractCoroutineInfoData(initialContinuation) return holder.extractCoroutineInfoData(initialContinuation)
} }
} }
@@ -50,15 +50,7 @@ class ManagerThreadExecutor(val debugProcess: DebugProcessImpl) {
} }
class ApplicationThreadExecutor { class ApplicationThreadExecutor {
fun <T> readAction(f: () -> T): T {
return ApplicationManager.getApplication().runReadAction(Computable(f))
}
fun schedule(f: () -> Unit, component: Component) { fun schedule(f: () -> Unit, component: Component) {
return ApplicationManager.getApplication().invokeLater({ f() }, ModalityState.stateForComponent(component)) return ApplicationManager.getApplication().invokeLater({ f() }, ModalityState.stateForComponent(component))
} }
fun schedule(f: () -> Unit) {
return ApplicationManager.getApplication().invokeLater { f() }
}
} }
@@ -6,16 +6,6 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy package org.jetbrains.kotlin.idea.debugger.coroutine.proxy
import com.intellij.debugger.jdi.StackFrameProxyImpl import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl
import com.sun.jdi.Location
import com.sun.jdi.StackFrame
class LocationStackFrameProxyImpl(val location: Location, frame: StackFrameProxyImpl) :
StackFrameProxyImpl(frame.threadProxy(), frame.stackFrame, frame.indexFromBottom) {
override fun location(): Location {
return location
}
}
class SkipCoroutineStackFrameProxyImpl(frame: StackFrameProxyImpl) : class SkipCoroutineStackFrameProxyImpl(frame: StackFrameProxyImpl) :
StackFrameProxyImpl(frame.threadProxy(), frame.stackFrame, frame.indexFromBottom) StackFrameProxyImpl(frame.threadProxy(), frame.stackFrame, frame.indexFromBottom)
@@ -13,26 +13,26 @@ import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
abstract class BaseDynamicMirror<T>(val value: ObjectReference, val name: String, val context: DefaultExecutionContext) { abstract class BaseDynamicMirror<T>(val value: ObjectReference, val name: String, val context: DefaultExecutionContext) {
val log by logger val log by logger
val cls: ReferenceType? = value.referenceType() private val cls: ReferenceType? = value.referenceType()
fun makeField(fieldName: String): Field? = private fun makeField(fieldName: String): Field? =
cls?.let { it.fieldByName(fieldName) } cls?.fieldByName(fieldName)
fun findMethod(methodName: String): Method? = fun findMethod(methodName: String): Method? =
cls?.let { it.methodsByName(methodName).single() } cls?.methodsByName(methodName)?.single()
fun findMethod(methodName: String, signature: String): Method? = fun findMethod(methodName: String, signature: String): Method? =
cls?.let { it.methodsByName(methodName, signature).single() } cls?.methodsByName(methodName, signature)?.single()
fun isCompatible(value: ObjectReference?) = fun isCompatible(value: ObjectReference?) =
value?.let { it.referenceType().isSubTypeOrSame(name) } ?: false value?.referenceType()?.isSubTypeOrSame(name) ?: false
fun mirror(): T? { fun mirror(): T? {
if (!isCompatible(value)) { return if (!isCompatible(value)) {
log.trace("Value ${value.referenceType()} is not compatible with $name.") log.trace("Value ${value.referenceType()} is not compatible with $name.")
return null null
} else } else
return fetchMirror(value, context) fetchMirror(value, context)
} }
fun staticObjectValue(fieldName: String): ObjectReference? { fun staticObjectValue(fieldName: String): ObjectReference? {
@@ -5,18 +5,16 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror package org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror
import com.sun.jdi.Method
import com.sun.jdi.ObjectReference import com.sun.jdi.ObjectReference
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
import java.lang.StackTraceElement
class CoroutineContext(context: DefaultExecutionContext) : class CoroutineContext(context: DefaultExecutionContext) :
BaseMirror<MirrorOfCoroutineContext>("kotlin.coroutines.CombinedContext", context) { BaseMirror<MirrorOfCoroutineContext>("kotlin.coroutines.CombinedContext", context) {
val coroutineNameRef = CoroutineName(context) private val coroutineNameRef = CoroutineName(context)
val coroutineIdRef = CoroutineId(context) private val coroutineIdRef = CoroutineId(context)
val jobRef = Job(context) private val jobRef = Job(context)
val dispatcherRef = CoroutineDispatcher(context) private val dispatcherRef = CoroutineDispatcher(context)
val getContextElement = makeMethod("get") private val getContextElement = makeMethod("get")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfCoroutineContext? { override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfCoroutineContext? {
val coroutineName = getElementValue(value, context, coroutineNameRef) val coroutineName = getElementValue(value, context, coroutineNameRef)
@@ -26,7 +24,7 @@ class CoroutineContext(context: DefaultExecutionContext) :
return MirrorOfCoroutineContext(value, coroutineName, coroutineId, dispatcher, job) return MirrorOfCoroutineContext(value, coroutineName, coroutineId, dispatcher, job)
} }
fun <T> getElementValue(value: ObjectReference, context: DefaultExecutionContext, keyProvider: ContextKey<T>): T? { private fun <T> getElementValue(value: ObjectReference, context: DefaultExecutionContext, keyProvider: ContextKey<T>): T? {
val key = keyProvider.key() ?: return null val key = keyProvider.key() ?: return null
val elementValue = objectValue(value, getContextElement, context, key) ?: return null val elementValue = objectValue(value, getContextElement, context, key) ?: return null
return keyProvider.mirror(elementValue, context) return keyProvider.mirror(elementValue, context)
@@ -42,12 +40,12 @@ data class MirrorOfCoroutineContext(
) )
abstract class ContextKey<T>(name: String, context: DefaultExecutionContext) : BaseMirror<T>(name, context) { abstract class ContextKey<T>(name: String, context: DefaultExecutionContext) : BaseMirror<T>(name, context) {
abstract fun key() : ObjectReference? abstract fun key(): ObjectReference?
} }
class CoroutineName(context: DefaultExecutionContext) : ContextKey<String>("kotlinx.coroutines.CoroutineName", context) { class CoroutineName(context: DefaultExecutionContext) : ContextKey<String>("kotlinx.coroutines.CoroutineName", context) {
val key = staticObjectValue("Key") val key = staticObjectValue("Key")
val getNameRef = makeMethod("getName") private val getNameRef = makeMethod("getName")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): String? { override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): String? {
return stringValue(value, getNameRef, context) return stringValue(value, getNameRef, context)
@@ -57,8 +55,8 @@ class CoroutineName(context: DefaultExecutionContext) : ContextKey<String>("kotl
} }
class CoroutineId(context: DefaultExecutionContext) : ContextKey<Long>("kotlinx.coroutines.CoroutineId", context) { class CoroutineId(context: DefaultExecutionContext) : ContextKey<Long>("kotlinx.coroutines.CoroutineId", context) {
val key = staticObjectValue("Key") private val key = staticObjectValue("Key")
val getIdRef = makeMethod("getId") private val getIdRef = makeMethod("getId")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): Long? { override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): Long? {
return longValue(value, getIdRef, context) return longValue(value, getIdRef, context)
@@ -79,8 +77,8 @@ class Job(context: DefaultExecutionContext) : ContextKey<ObjectReference>("kotli
class CoroutineDispatcher(context: DefaultExecutionContext) : ContextKey<String>("kotlinx.coroutines.CoroutineDispatcher", context) { class CoroutineDispatcher(context: DefaultExecutionContext) : ContextKey<String>("kotlinx.coroutines.CoroutineDispatcher", context) {
val key = staticObjectValue("Key") private val key = staticObjectValue("Key")
val jlm = JavaLangMirror(context) private val jlm = JavaLangMirror(context)
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): String? { override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): String? {
return jlm.string(value, context) return jlm.string(value, context)
@@ -5,29 +5,31 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror package org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror
import com.sun.jdi.* import com.sun.jdi.ObjectReference
import com.sun.jdi.ThreadReference
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isSubTypeOrSame import org.jetbrains.kotlin.idea.debugger.coroutine.util.isSubTypeOrSame
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
class DebugProbesImpl private constructor(context: DefaultExecutionContext) : class DebugProbesImpl private constructor(context: DefaultExecutionContext) :
BaseMirror<MirrorOfDebugProbesImpl>("kotlinx.coroutines.debug.internal.DebugProbesImpl", context) { BaseMirror<MirrorOfDebugProbesImpl>("kotlinx.coroutines.debug.internal.DebugProbesImpl", context) {
val javaLangListMirror = JavaUtilAbstractCollection(context) private val javaLangListMirror = JavaUtilAbstractCollection(context)
val stackTraceElement = StackTraceElement(context) private val stackTraceElement = StackTraceElement(context)
val coroutineInfo = CoroutineInfo.instance(this, context) ?: throw IllegalStateException("CoroutineInfo implementation not found.") private val coroutineInfo =
val debugProbesCoroutineOwner = DebugProbesImpl_CoroutineOwner(coroutineInfo, context) CoroutineInfo.instance(this, context) ?: throw IllegalStateException("CoroutineInfo implementation not found.")
val instance = staticObjectValue("INSTANCE") private val debugProbesCoroutineOwner = DebugProbesImplCoroutineOwner(coroutineInfo, context)
val isInstalledMethod = makeMethod("isInstalled\$kotlinx_coroutines_debug", "()Z") private val instance = staticObjectValue("INSTANCE")
private val isInstalledMethod = makeMethod("isInstalled\$kotlinx_coroutines_debug", "()Z")
?: makeMethod("isInstalled\$kotlinx_coroutines_core", "()Z") ?: throw IllegalStateException("isInstalledMethod not found") ?: makeMethod("isInstalled\$kotlinx_coroutines_core", "()Z") ?: throw IllegalStateException("isInstalledMethod not found")
val isInstalledValue = booleanValue(instance, isInstalledMethod, context) val isInstalledValue = booleanValue(instance, isInstalledMethod, context)
val enhanceStackTraceWithThreadDumpMethod = makeMethod("enhanceStackTraceWithThreadDump") private val enhanceStackTraceWithThreadDumpMethod = makeMethod("enhanceStackTraceWithThreadDump")
val dumpMethod = makeMethod("dumpCoroutinesInfo", "()Ljava/util/List;") private val dumpMethod = makeMethod("dumpCoroutinesInfo", "()Ljava/util/List;")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfDebugProbesImpl? { override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfDebugProbesImpl? {
return MirrorOfDebugProbesImpl(value, instance, isInstalledValue) return MirrorOfDebugProbesImpl(value, instance, isInstalledValue)
} }
fun enchanceStackTraceWithThreadDump( fun enhanceStackTraceWithThreadDump(
context: DefaultExecutionContext, context: DefaultExecutionContext,
coroutineInfo: ObjectReference, coroutineInfo: ObjectReference,
lastObservedStackTrace: ObjectReference lastObservedStackTrace: ObjectReference
@@ -57,12 +59,13 @@ class DebugProbesImpl private constructor(context: DefaultExecutionContext) :
try { try {
DebugProbesImpl(context) DebugProbesImpl(context)
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
log.debug("Attempt to access DebugProbesImpl but none found.", e)
null null
} }
} }
} }
class DebugProbesImpl_CoroutineOwner(val coroutineInfo: CoroutineInfo, context: DefaultExecutionContext) : class DebugProbesImplCoroutineOwner(private val coroutineInfo: CoroutineInfo, context: DefaultExecutionContext) :
BaseMirror<MirrorOfCoroutineOwner>(COROUTINE_OWNER_CLASS_NAME, context) { BaseMirror<MirrorOfCoroutineOwner>(COROUTINE_OWNER_CLASS_NAME, context) {
private val infoField = makeField("info") private val infoField = makeField("info")
@@ -75,7 +78,7 @@ class DebugProbesImpl_CoroutineOwner(val coroutineInfo: CoroutineInfo, context:
const val COROUTINE_OWNER_CLASS_NAME = "kotlinx.coroutines.debug.internal.DebugProbesImpl\$CoroutineOwner" const val COROUTINE_OWNER_CLASS_NAME = "kotlinx.coroutines.debug.internal.DebugProbesImpl\$CoroutineOwner"
fun instanceOf(value: ObjectReference?) = fun instanceOf(value: ObjectReference?) =
value?.let { it.referenceType().isSubTypeOrSame(COROUTINE_OWNER_CLASS_NAME) } ?: false value?.referenceType()?.isSubTypeOrSame(COROUTINE_OWNER_CLASS_NAME) ?: false
} }
} }
@@ -84,13 +87,13 @@ data class MirrorOfCoroutineOwner(val that: ObjectReference, val coroutineInfo:
data class MirrorOfDebugProbesImpl(val that: ObjectReference, val instance: ObjectReference?, val isInstalled: Boolean?) data class MirrorOfDebugProbesImpl(val that: ObjectReference, val instance: ObjectReference?, val isInstalled: Boolean?)
class CoroutineInfo private constructor( class CoroutineInfo private constructor(
val debugProbesImplMirror: DebugProbesImpl, private val debugProbesImplMirror: DebugProbesImpl,
context: DefaultExecutionContext, context: DefaultExecutionContext,
val className: String = AGENT_134_CLASS_NAME val className: String = AGENT_134_CLASS_NAME
) : ) :
BaseMirror<MirrorOfCoroutineInfo>(className, context) { BaseMirror<MirrorOfCoroutineInfo>(className, context) {
val javaLangMirror = JavaLangMirror(context) private val javaLangMirror = JavaLangMirror(context)
val javaLangListMirror = JavaUtilAbstractCollection(context) private val javaLangListMirror = JavaUtilAbstractCollection(context)
private val coroutineContextMirror = CoroutineContext(context) private val coroutineContextMirror = CoroutineContext(context)
private val stackTraceElement = StackTraceElement(context) private val stackTraceElement = StackTraceElement(context)
private val contextFieldRef = makeField("context") private val contextFieldRef = makeField("context")
@@ -105,16 +108,16 @@ class CoroutineInfo private constructor(
companion object { companion object {
val log by logger val log by logger
const val AGENT_134_CLASS_NAME = "kotlinx.coroutines.debug.CoroutineInfo" private const val AGENT_134_CLASS_NAME = "kotlinx.coroutines.debug.CoroutineInfo"
const val AGENT_135_AND_UP_CLASS_NAME = "kotlinx.coroutines.debug.internal.DebugCoroutineInfo" private const val AGENT_135_AND_UP_CLASS_NAME = "kotlinx.coroutines.debug.internal.DebugCoroutineInfo"
fun instance(debugProbesImplMirror: DebugProbesImpl, context: DefaultExecutionContext): CoroutineInfo? { fun instance(debugProbesImplMirror: DebugProbesImpl, context: DefaultExecutionContext): CoroutineInfo? {
val classType = context.findClassSafe(AGENT_134_CLASS_NAME) ?: context.findClassSafe(AGENT_135_AND_UP_CLASS_NAME) ?: return null val classType = context.findClassSafe(AGENT_134_CLASS_NAME) ?: context.findClassSafe(AGENT_135_AND_UP_CLASS_NAME) ?: return null
try { return try {
return CoroutineInfo(debugProbesImplMirror, context, classType.name()) CoroutineInfo(debugProbesImplMirror, context, classType.name())
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
log.warn("coroutine-debugger: $classType not found", e) log.warn("coroutine-debugger: $classType not found", e)
return null null
} }
} }
} }
@@ -133,9 +136,9 @@ class CoroutineInfo private constructor(
val creationStackTrace = creationStackTraceMirror?.values?.mapNotNull { stackTraceElement.mirror(it, context) } val creationStackTrace = creationStackTraceMirror?.values?.mapNotNull { stackTraceElement.mirror(it, context) }
val lastObservedStackTrace = objectValue(value, lastObservedStackTraceMethod, context) val lastObservedStackTrace = objectValue(value, lastObservedStackTraceMethod, context)
val enchancedList = val enhancedList =
if (lastObservedStackTrace != null) if (lastObservedStackTrace != null)
debugProbesImplMirror.enchanceStackTraceWithThreadDump(context, value, lastObservedStackTrace) debugProbesImplMirror.enhanceStackTraceWithThreadDump(context, value, lastObservedStackTrace)
else emptyList() else emptyList()
val lastObservedThread = threadValue(value, lastObservedThreadField) val lastObservedThread = threadValue(value, lastObservedThreadField)
val lastObservedFrame = threadValue(value, lastObservedFrameField) val lastObservedFrame = threadValue(value, lastObservedFrameField)
@@ -144,7 +147,7 @@ class CoroutineInfo private constructor(
coroutineContext, coroutineContext,
creationStackBottom, creationStackBottom,
sequenceNumber, sequenceNumber,
enchancedList, enhancedList,
creationStackTrace, creationStackTrace,
state, state,
lastObservedThread, lastObservedThread,
@@ -159,7 +162,7 @@ data class MirrorOfCoroutineInfo(
val context: MirrorOfCoroutineContext?, val context: MirrorOfCoroutineContext?,
val creationStackBottom: MirrorOfCoroutineStackFrame?, val creationStackBottom: MirrorOfCoroutineStackFrame?,
val sequenceNumber: Long?, val sequenceNumber: Long?,
val enchancedStackTrace: List<MirrorOfStackTraceElement>?, val enhancedStackTrace: List<MirrorOfStackTraceElement>?,
val creationStackTrace: List<MirrorOfStackTraceElement>?, val creationStackTrace: List<MirrorOfStackTraceElement>?,
val state: String?, val state: String?,
val lastObservedThread: ThreadReference?, val lastObservedThread: ThreadReference?,
@@ -234,11 +237,8 @@ data class MirrorOfStackTraceElement(
val lineNumber: Int?, val lineNumber: Int?,
val format: Byte? val format: Byte?
) { ) {
fun isInvokeSuspend() =
"invokeSuspend" == methodName
fun stackTraceElement() = fun stackTraceElement() =
java.lang.StackTraceElement( StackTraceElement(
declaringClass, declaringClass,
methodName, methodName,
fileName, fileName,
@@ -9,36 +9,35 @@ import com.sun.jdi.*
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isSubTypeOrSame import org.jetbrains.kotlin.idea.debugger.coroutine.util.isSubTypeOrSame
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
import java.lang.IllegalStateException
abstract class BaseMirror<T>(val name: String, context: DefaultExecutionContext) { abstract class BaseMirror<T>(val name: String, context: DefaultExecutionContext) {
val log by logger val log by logger
protected val cls = context.findClassSafe(name) ?: throw IllegalStateException("coroutine-debugger: class $name not found.") protected val cls = context.findClassSafe(name) ?: throw IllegalStateException("coroutine-debugger: class $name not found.")
fun makeField(fieldName: String): Field? = fun makeField(fieldName: String): Field? =
cls?.let { it.fieldByName(fieldName) } cls.fieldByName(fieldName)
fun makeMethod(methodName: String): Method? = fun makeMethod(methodName: String): Method? =
cls?.let { it.methodsByName(methodName).singleOrNull() } cls.methodsByName(methodName).singleOrNull()
fun makeMethod(methodName: String, signature: String): Method? = fun makeMethod(methodName: String, signature: String): Method? =
cls?.let { it.methodsByName(methodName, signature).singleOrNull() } cls.methodsByName(methodName, signature).singleOrNull()
fun isCompatible(value: ObjectReference?) = fun isCompatible(value: ObjectReference?) =
value?.let { it.referenceType().isSubTypeOrSame(name) } ?: false value?.referenceType()?.isSubTypeOrSame(name) ?: false
fun mirror(value: ObjectReference?, context: DefaultExecutionContext): T? { fun mirror(value: ObjectReference?, context: DefaultExecutionContext): T? {
value ?: return null value ?: return null
if (!isCompatible(value)) { return if (!isCompatible(value)) {
log.trace("Value ${value.referenceType()} is not compatible with $name.") log.trace("Value ${value.referenceType()} is not compatible with $name.")
return null null
} else } else
return fetchMirror(value, context) fetchMirror(value, context)
} }
fun staticObjectValue(fieldName: String): ObjectReference? { fun staticObjectValue(fieldName: String): ObjectReference? {
val keyFieldRef = makeField(fieldName) val keyFieldRef = makeField(fieldName)
return cls?.let { it.getValue(keyFieldRef) as? ObjectReference } return cls.let { it.getValue(keyFieldRef) as? ObjectReference }
} }
fun staticMethodValue(instance: ObjectReference?, method: Method?, context: DefaultExecutionContext, vararg values: Value?) = fun staticMethodValue(instance: ObjectReference?, method: Method?, context: DefaultExecutionContext, vararg values: Value?) =
@@ -49,7 +48,7 @@ abstract class BaseMirror<T>(val name: String, context: DefaultExecutionContext)
} }
fun staticMethodValue(method: Method?, context: DefaultExecutionContext, vararg values: Value?) = fun staticMethodValue(method: Method?, context: DefaultExecutionContext, vararg values: Value?) =
cls?.let { cls.let {
method?.let { method?.let {
context.invokeMethodSafe(cls, method, values.asList()) as? ObjectReference context.invokeMethodSafe(cls, method, values.asList()) as? ObjectReference
} }
@@ -109,7 +108,7 @@ abstract class BaseMirror<T>(val name: String, context: DefaultExecutionContext)
protected abstract fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): T? protected abstract fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): T?
} }
class StandaloneCoroutine(context: DefaultExecutionContext) : class StandaloneCoroutine private constructor(context: DefaultExecutionContext) :
BaseMirror<MirrorOfStandaloneCoroutine>("kotlinx.coroutines.StandaloneCoroutine", context) { BaseMirror<MirrorOfStandaloneCoroutine>("kotlinx.coroutines.StandaloneCoroutine", context) {
private val coroutineContextMirror = CoroutineContext(context) private val coroutineContextMirror = CoroutineContext(context)
private val childContinuationMirror = ChildContinuation(context) private val childContinuationMirror = ChildContinuation(context)
@@ -118,19 +117,22 @@ class StandaloneCoroutine(context: DefaultExecutionContext) :
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfStandaloneCoroutine { override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfStandaloneCoroutine {
val state = objectValue(value, stateFieldRef) val state = objectValue(value, stateFieldRef)
val childcontinuation = childContinuationMirror.mirror(state, context) val childContinuation = childContinuationMirror.mirror(state, context)
val cc = objectValue(value, contextFieldRef) val cc = objectValue(value, contextFieldRef)
val coroutineContext = coroutineContextMirror.mirror(cc, context) val coroutineContext = coroutineContextMirror.mirror(cc, context)
return MirrorOfStandaloneCoroutine(value, childcontinuation, coroutineContext) return MirrorOfStandaloneCoroutine(value, childContinuation, coroutineContext)
} }
companion object { companion object {
val log by logger
fun instance(context: DefaultExecutionContext): StandaloneCoroutine? { fun instance(context: DefaultExecutionContext): StandaloneCoroutine? {
val sc = StandaloneCoroutine(context) return try {
if (sc.cls == null) StandaloneCoroutine(context)
return null } catch (e: IllegalStateException) {
else log.debug("Attempt to access DebugProbesImpl but none found.", e)
return sc null
}
} }
} }
} }
@@ -160,7 +162,7 @@ data class MirrorOfChildContinuation(
class CancellableContinuationImpl(context: DefaultExecutionContext) : class CancellableContinuationImpl(context: DefaultExecutionContext) :
BaseMirror<MirrorOfCancellableContinuationImpl>("kotlinx.coroutines.CancellableContinuationImpl", context) { BaseMirror<MirrorOfCancellableContinuationImpl>("kotlinx.coroutines.CancellableContinuationImpl", context) {
private val coroutineContextMirror = CoroutineContext(context) private val coroutineContextMirror = CoroutineContext(context)
private val dispatchedContinuationtMirror = DispatchedContinuation(context) private val dispatchedContinuationMirror = DispatchedContinuation(context)
private val decisionFieldRef = makeField("_decision") private val decisionFieldRef = makeField("_decision")
private val delegateFieldRef = makeField("delegate") // DispatchedContinuation private val delegateFieldRef = makeField("delegate") // DispatchedContinuation
private val resumeModeFieldRef = makeField("resumeMode") private val resumeModeFieldRef = makeField("resumeMode")
@@ -169,7 +171,7 @@ class CancellableContinuationImpl(context: DefaultExecutionContext) :
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfCancellableContinuationImpl? { override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfCancellableContinuationImpl? {
val decision = intValue(value, decisionFieldRef) val decision = intValue(value, decisionFieldRef)
val dispatchedContinuation = dispatchedContinuationtMirror.mirror(objectValue(value, delegateFieldRef), context) val dispatchedContinuation = dispatchedContinuationMirror.mirror(objectValue(value, delegateFieldRef), context)
val submissionTime = longValue(value, submissionTimeFieldRef) val submissionTime = longValue(value, submissionTimeFieldRef)
val resumeMode = intValue(value, resumeModeFieldRef) val resumeMode = intValue(value, resumeModeFieldRef)
val coroutineContext = objectValue(value, contextFieldRef) val coroutineContext = objectValue(value, contextFieldRef)
@@ -183,7 +185,7 @@ data class MirrorOfCancellableContinuationImpl(
val decision: Int?, val decision: Int?,
val delegate: MirrorOfDispatchedContinuation?, val delegate: MirrorOfDispatchedContinuation?,
val resumeMode: Int?, val resumeMode: Int?,
val submissionTyme: Long?, val submissionTime: Long?,
val jobContext: MirrorOfCoroutineContext? val jobContext: MirrorOfCoroutineContext?
) )
@@ -45,7 +45,7 @@ class DebugMetadata private constructor(context: DefaultExecutionContext) :
try { try {
return DebugMetadata(context) return DebugMetadata(context)
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
log.warn("Attempt to access DebugMetadata", e) log.debug("Attempt to access DebugMetadata but none found.", e)
} }
return null return null
} }
@@ -73,7 +73,8 @@ class BaseContinuationImpl(context: DefaultExecutionContext, private val debugMe
val fieldVariables = getSpilledVariableFieldMapping(value, context) val fieldVariables = getSpilledVariableFieldMapping(value, context)
val completionValue = objectValue(value, getCompletion, context) val completionValue = objectValue(value, getCompletion, context)
val completion = if (completionValue != null && isCompatible(completionValue)) completionValue else null val completion = if (completionValue != null && isCompatible(completionValue)) completionValue else null
val coroutineOwner = if (completionValue != null && DebugProbesImpl_CoroutineOwner.instanceOf(completionValue)) completionValue else null val coroutineOwner =
if (completionValue != null && DebugProbesImplCoroutineOwner.instanceOf(completionValue)) completionValue else null
return MirrorOfBaseContinuationImpl(value, stackTraceElementMirror, fieldVariables, completion, coroutineOwner) return MirrorOfBaseContinuationImpl(value, stackTraceElementMirror, fieldVariables, completion, coroutineOwner)
} }
@@ -84,7 +85,7 @@ class BaseContinuationImpl(context: DefaultExecutionContext, private val debugMe
val length = getSpilledVariableFieldMappingReference.length() / 2 val length = getSpilledVariableFieldMappingReference.length() / 2
val fieldVariables = ArrayList<FieldVariable>() val fieldVariables = ArrayList<FieldVariable>()
for (index in 0 until length) { for (index in 0 until length) {
var fieldVariable = getFieldVariableName(getSpilledVariableFieldMappingReference, index) ?: continue val fieldVariable = getFieldVariableName(getSpilledVariableFieldMappingReference, index) ?: continue
fieldVariables.add(fieldVariable) fieldVariables.add(fieldVariable)
} }
return fieldVariables return fieldVariables
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
class JavaLangMirror(context: DefaultExecutionContext) { class JavaLangMirror(context: DefaultExecutionContext) {
// java.lang.Object // java.lang.Object
val classClsRef = context.findClass("java.lang.Object") as ClassType private val classClsRef = context.findClass("java.lang.Object") as ClassType
val toString: Method = classClsRef.concreteMethodByName("toString", "()Ljava/lang/String;") val toString: Method = classClsRef.concreteMethodByName("toString", "()Ljava/lang/String;")
// java.util.List // java.util.List
@@ -25,9 +25,6 @@ class JavaLangMirror(context: DefaultExecutionContext) {
private val fileNameFieldRef: Field = stackTraceElementClsRef.fieldByName("fileName") private val fileNameFieldRef: Field = stackTraceElementClsRef.fieldByName("fileName")
private val lineNumberFieldRef: Field = stackTraceElementClsRef.fieldByName("lineNumber") private val lineNumberFieldRef: Field = stackTraceElementClsRef.fieldByName("lineNumber")
// java.lang.Class
val classType = context.findClass("java.lang.Class") as ClassType
fun string(state: ObjectReference, context: DefaultExecutionContext): String = fun string(state: ObjectReference, context: DefaultExecutionContext): String =
(context.invokeMethod(state, toString, emptyList()) as StringReference).value() (context.invokeMethod(state, toString, emptyList()) as StringReference).value()
@@ -63,14 +60,14 @@ class JavaLangMirror(context: DefaultExecutionContext) {
class JavaUtilAbstractCollection(context: DefaultExecutionContext) : class JavaUtilAbstractCollection(context: DefaultExecutionContext) :
BaseMirror<MirrorOfJavaLangAbstractCollection>("java.util.AbstractCollection", context) { BaseMirror<MirrorOfJavaLangAbstractCollection>("java.util.AbstractCollection", context) {
val abstractList = JavaUtilAbstractList(context) private val abstractList = JavaUtilAbstractList(context)
val sizeMethod = makeMethod("size") private val sizeMethod = makeMethod("size")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfJavaLangAbstractCollection? { override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfJavaLangAbstractCollection? {
val list = mutableListOf<ObjectReference>() val list = mutableListOf<ObjectReference>()
val size = intValue(value, sizeMethod, context) ?: 0 val size = intValue(value, sizeMethod, context) ?: 0
for (index in 0 until size) { for (index in 0 until size) {
val reference = abstractList.get(value, index, context) ?: continue val reference = abstractList.get(value, index, context) ?: continue
list.add(reference) list.add(reference)
} }
return MirrorOfJavaLangAbstractCollection(value, list) return MirrorOfJavaLangAbstractCollection(value, list)
@@ -81,7 +78,7 @@ class JavaUtilAbstractList(context: DefaultExecutionContext) :
BaseMirror<ObjectReference>("java.util.AbstractList", context) { BaseMirror<ObjectReference>("java.util.AbstractList", context) {
val getMethod = makeMethod("get") val getMethod = makeMethod("get")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext) = override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): Nothing? =
null null
fun get(value: ObjectReference, index: Int, context: DefaultExecutionContext): ObjectReference? = fun get(value: ObjectReference, index: Int, context: DefaultExecutionContext): ObjectReference? =
@@ -20,7 +20,7 @@ class CoroutineFrameBuilder {
companion object { companion object {
val log by logger val log by logger
const val PRE_FETCH_FRAME_COUNT = 5 private const val PRE_FETCH_FRAME_COUNT = 5
fun build(coroutine: CoroutineInfoData, suspendContext: SuspendContextImpl): DoubleFrameList? = fun build(coroutine: CoroutineInfoData, suspendContext: SuspendContextImpl): DoubleFrameList? =
when { when {
@@ -81,10 +81,10 @@ class CoroutineFrameBuilder {
frame: StackFrameProxyImpl frame: StackFrameProxyImpl
): RunningCoroutineStackFrameItem? { ): RunningCoroutineStackFrameItem? {
val location = frame.location() val location = frame.location()
if (!location.safeCoroutineExitPointLineNumber()) return if (!location.safeCoroutineExitPointLineNumber())
return RunningCoroutineStackFrameItem(frame, location) RunningCoroutineStackFrameItem(frame, location)
else else
return null null
} }
/** /**
@@ -130,7 +130,7 @@ class CoroutineFrameBuilder {
else -> null else -> null
} ?: return null } ?: return null
val continuationHolder = ContinuationHolder.instance(context) ?: return null val continuationHolder = ContinuationHolder.instance(context)
val coroutineInfo = continuationHolder.extractCoroutineInfoData(continuation) ?: return null val coroutineInfo = continuationHolder.extractCoroutineInfoData(continuation) ?: return null
return preflight(frame, theFollowingFrames, coroutineInfo, mode) return preflight(frame, theFollowingFrames, coroutineInfo, mode)
} }
@@ -139,7 +139,7 @@ class CoroutineFrameBuilder {
private fun lookForTheFollowingFrame(theFollowingFrames: List<StackFrameProxyImpl>): StackFrameProxyImpl? { private fun lookForTheFollowingFrame(theFollowingFrames: List<StackFrameProxyImpl>): StackFrameProxyImpl? {
for (i in 0 until min(PRE_FETCH_FRAME_COUNT, theFollowingFrames.size)) { // pre-scan PRE_FETCH_FRAME_COUNT frames for (i in 0 until min(PRE_FETCH_FRAME_COUNT, theFollowingFrames.size)) { // pre-scan PRE_FETCH_FRAME_COUNT frames
val nextFrame = theFollowingFrames.get(i) val nextFrame = theFollowingFrames[i]
if (nextFrame.location().isPreFlight() == SuspendExitMode.SUSPEND_METHOD) { if (nextFrame.location().isPreFlight() == SuspendExitMode.SUSPEND_METHOD) {
return nextFrame return nextFrame
} }
@@ -168,7 +168,7 @@ class CoroutineFrameBuilder {
private fun getThisContinuation(frame: StackFrameProxyImpl?): ObjectReference? = private fun getThisContinuation(frame: StackFrameProxyImpl?): ObjectReference? =
frame?.thisVariableValue() frame?.thisVariableValue()
fun theFollowingFrames(frame: StackFrameProxyImpl): List<StackFrameProxyImpl>? { private fun theFollowingFrames(frame: StackFrameProxyImpl): List<StackFrameProxyImpl>? {
val frames = frame.threadProxy().frames() val frames = frame.threadProxy().frames()
val indexOfCurrentFrame = frames.indexOf(frame) val indexOfCurrentFrame = frames.indexOf(frame)
if (indexOfCurrentFrame >= 0) { if (indexOfCurrentFrame >= 0) {
@@ -30,9 +30,6 @@ fun Method.isInvokeSuspend(): Boolean =
fun Method.isInvoke(): Boolean = fun Method.isInvoke(): Boolean =
name() == "invoke" && signature().contains("Ljava/lang/Object;)Ljava/lang/Object;") name() == "invoke" && signature().contains("Ljava/lang/Object;)Ljava/lang/Object;")
fun Method.isContinuation() =
isInvokeSuspend() && declaringType().isContinuation() /* Perhaps need to check for "Lkotlin/coroutines/Continuation;)" in signature() ? */
fun Method.isSuspendLambda() = fun Method.isSuspendLambda() =
isInvokeSuspend() && declaringType().isSuspendLambda() isInvokeSuspend() && declaringType().isSuspendLambda()
@@ -79,16 +76,13 @@ fun StackFrameProxyImpl.variableValue(variableName: String): ObjectReference? {
return getValue(continuationVariable) as? ObjectReference ?: return null return getValue(continuationVariable) as? ObjectReference ?: return null
} }
fun StackFrameProxyImpl.completionVariableValue(): ObjectReference? =
variableValue("completion")
fun StackFrameProxyImpl.continuationVariableValue(): ObjectReference? = fun StackFrameProxyImpl.continuationVariableValue(): ObjectReference? =
variableValue("\$continuation") variableValue("\$continuation")
fun StackFrameProxyImpl.thisVariableValue(): ObjectReference? = fun StackFrameProxyImpl.thisVariableValue(): ObjectReference? =
this.thisObject() this.thisObject()
private fun Method.isGetCOROUTINE_SUSPENDED() = private fun Method.isGetCoroutineSuspended() =
signature() == "()Ljava/lang/Object;" && name() == "getCOROUTINE_SUSPENDED" && declaringType().name() == "kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsKt" signature() == "()Ljava/lang/Object;" && name() == "getCOROUTINE_SUSPENDED" && declaringType().name() == "kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsKt"
fun DefaultExecutionContext.findCoroutineMetadataType() = fun DefaultExecutionContext.findCoroutineMetadataType() =
@@ -101,7 +95,7 @@ fun DefaultExecutionContext.findCancellableContinuationImplReferenceType(): List
vm.classesByName("kotlinx.coroutines.CancellableContinuationImpl") vm.classesByName("kotlinx.coroutines.CancellableContinuationImpl")
fun hasGetCoroutineSuspended(frames: List<StackFrameProxyImpl>) = fun hasGetCoroutineSuspended(frames: List<StackFrameProxyImpl>) =
frames.indexOfFirst { it.safeLocation()?.safeMethod()?.isGetCOROUTINE_SUSPENDED() == true } frames.indexOfFirst { it.safeLocation()?.safeMethod()?.isGetCoroutineSuspended() == true }
fun StackTraceElement.isCreationSeparatorFrame() = fun StackTraceElement.isCreationSeparatorFrame() =
className.startsWith(CREATION_STACK_TRACE_SEPARATOR) className.startsWith(CREATION_STACK_TRACE_SEPARATOR)
@@ -112,9 +106,6 @@ fun StackTraceElement.findPosition(project: Project): XSourcePosition? =
fun Location.findPosition(project: Project) = fun Location.findPosition(project: Project) =
getPosition(project, declaringType().name(), lineNumber()) getPosition(project, declaringType().name(), lineNumber())
fun ClassType.completionField() =
fieldByName("completion")
private fun getPosition(project: Project, className: String, lineNumber: Int): XSourcePosition? { private fun getPosition(project: Project, className: String, lineNumber: Int): XSourcePosition? {
val psiFacade = JavaPsiFacade.getInstance(project) val psiFacade = JavaPsiFacade.getInstance(project)
val psiClass = psiFacade.findClass( val psiClass = psiFacade.findClass(
@@ -130,7 +121,7 @@ private fun getPosition(project: Project, className: String, lineNumber: Int): X
fun SuspendContextImpl.executionContext() = fun SuspendContextImpl.executionContext() =
invokeInManagerThread { DefaultExecutionContext(EvaluationContextImpl(this, this.frameProxy)) } invokeInManagerThread { DefaultExecutionContext(EvaluationContextImpl(this, this.frameProxy)) }
fun <T : Any> SuspendContextImpl.invokeInManagerThread(f: () -> T?) : T? = fun <T : Any> SuspendContextImpl.invokeInManagerThread(f: () -> T?): T? =
debugProcess.invokeInManagerThread { f() } debugProcess.invokeInManagerThread { f() }
fun ThreadReferenceProxyImpl.supportsEvaluation(): Boolean = fun ThreadReferenceProxyImpl.supportsEvaluation(): Boolean =
@@ -10,37 +10,16 @@ import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.memory.utils.StackFrameItem import com.intellij.debugger.memory.utils.StackFrameItem
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.MessageType
import com.intellij.openapi.util.registry.Registry
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.xdebugger.XDebuggerUtil import com.intellij.xdebugger.XDebuggerUtil
import com.intellij.xdebugger.XSourcePosition import com.intellij.xdebugger.XSourcePosition
import com.intellij.xdebugger.frame.XStackFrame
import com.intellij.xdebugger.impl.XDebuggerManagerImpl
import com.sun.jdi.Location import com.sun.jdi.Location
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ApplicationThreadExecutor import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ApplicationThreadExecutor
import org.jetbrains.kotlin.idea.debugger.safeLineNumber import org.jetbrains.kotlin.idea.debugger.safeLineNumber
import org.jetbrains.kotlin.idea.debugger.safeLocation import org.jetbrains.kotlin.idea.debugger.safeLocation
import org.jetbrains.kotlin.idea.debugger.safeMethod import org.jetbrains.kotlin.idea.debugger.safeMethod
fun getPosition(stackTraceElement: StackTraceElement, project: Project): XSourcePosition? {
val psiFacade = JavaPsiFacade.getInstance(project)
val psiClass = ApplicationThreadExecutor().readAction {
@Suppress("DEPRECATION")
psiFacade.findClass(
stackTraceElement.className.substringBefore("$"), // find outer class, for which psi exists TODO
GlobalSearchScope.everythingScope(project)
)
}
val classFile = psiClass?.containingFile?.virtualFile
// to convert to 0-based line number or '-1' to do not move
val lineNumber = if (stackTraceElement.lineNumber > 0) stackTraceElement.lineNumber - 1 else return null
return XDebuggerUtil.getInstance().createPosition(classFile, lineNumber)
}
fun Location.format(): String { fun Location.format(): String {
val method = safeMethod() val method = safeMethod()
return "${method?.name() ?: "noname"}:${safeLineNumber()}, ${method?.declaringType()?.name() ?: "empty"}" return "${method?.name() ?: "noname"}:${safeLineNumber()}, ${method?.declaringType()?.name() ?: "empty"}"
@@ -48,7 +27,7 @@ fun Location.format(): String {
fun JavaStackFrame.format(): String { fun JavaStackFrame.format(): String {
val location = descriptor.location val location = descriptor.location
return location?.let { it.format() } ?: "emptyLocation" return location?.format() ?: "emptyLocation"
} }
fun StackFrameItem.format(): String { fun StackFrameItem.format(): String {
@@ -63,5 +42,3 @@ fun StackFrameProxyImpl.format(): String {
} }
fun isInUnitTest() = ApplicationManager.getApplication().isUnitTestMode fun isInUnitTest() = ApplicationManager.getApplication().isUnitTestMode
fun coroutineDebuggerTraceEnabled() = Registry.`is`("kotlin.debugger.coroutines.trace") || isInUnitTest()
@@ -5,27 +5,29 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.util package org.jetbrains.kotlin.idea.debugger.coroutine.util
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.DefaultLogger
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.xdebugger.XDebugSession import com.intellij.xdebugger.XDebugSession
import com.intellij.xdebugger.XDebugSessionListener import com.intellij.xdebugger.XDebugSessionListener
import org.apache.log4j.Level
import org.jetbrains.kotlin.idea.util.application.isUnitTestMode
import javax.swing.Icon import javax.swing.Icon
import javax.swing.JComponent import javax.swing.JComponent
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
interface CreateContentParamsProvider { interface CreateContentParamsProvider {
fun createContentParams() : CreateContentParams fun createContentParams(): CreateContentParams
} }
data class CreateContentParams(val id: String, val component: JComponent, val displayName: String, val icon: Icon?, val parentComponent: JComponent) data class CreateContentParams(
val id: String,
val component: JComponent,
val displayName: String,
val icon: Icon?,
val parentComponent: JComponent
)
interface XDebugSessionListenerProvider { interface XDebugSessionListenerProvider {
fun debugSessionListener(session: XDebugSession) : XDebugSessionListener fun debugSessionListener(session: XDebugSession): XDebugSessionListener
} }
/** /**
@@ -45,7 +45,12 @@ import javax.swing.event.DocumentEvent
/** /**
* Panel with dump of coroutines * Panel with dump of coroutines
*/ */
class CoroutineDumpPanel(project: Project, consoleView: ConsoleView, toolbarActions: DefaultActionGroup, val dump: List<CoroutineInfoData>) : class CoroutineDumpPanel(
project: Project,
consoleView: ConsoleView,
toolbarActions: DefaultActionGroup,
val dump: List<CoroutineInfoData>
) :
JPanel(BorderLayout()), DataProvider { JPanel(BorderLayout()), DataProvider {
private var exporterToTextFile: ExporterToTextFile private var exporterToTextFile: ExporterToTextFile
private var mergedDump = ArrayList<CoroutineInfoData>() private var mergedDump = ArrayList<CoroutineInfoData>()
@@ -180,14 +185,6 @@ class CoroutineDumpPanel(project: Project, consoleView: ConsoleView, toolbarActi
override fun getData(dataId: String): Any? = if (PlatformDataKeys.EXPORTER_TO_TEXT_FILE.`is`(dataId)) exporterToTextFile else null override fun getData(dataId: String): Any? = if (PlatformDataKeys.EXPORTER_TO_TEXT_FILE.`is`(dataId)) exporterToTextFile else null
private fun getCoroutineStateIcon(infoData: CoroutineInfoData): Icon {
return when (infoData.key.state) {
State.RUNNING -> LayeredIcon(AllIcons.Actions.Resume, Daemon_sign)
State.SUSPENDED -> AllIcons.Actions.Pause
else -> EmptyIcon.create(6)
}
}
private fun getAttributes(infoData: CoroutineInfoData): SimpleTextAttributes { private fun getAttributes(infoData: CoroutineInfoData): SimpleTextAttributes {
return when { return when {
infoData.isSuspended() -> SimpleTextAttributes.GRAY_ATTRIBUTES infoData.isSuspended() -> SimpleTextAttributes.GRAY_ATTRIBUTES
@@ -296,9 +293,9 @@ class CoroutineDumpPanel(project: Project, consoleView: ConsoleView, toolbarActi
private fun stringStackTrace(info: CoroutineInfoData) = private fun stringStackTrace(info: CoroutineInfoData) =
buildString { buildString {
appendln("\"${info.key.name}\", state: ${info.key.state}") appendLine("\"${info.key.name}\", state: ${info.key.state}")
info.stackTrace.forEach { info.stackTrace.forEach {
appendln("\t$it") appendLine("\t$it")
} }
} }
@@ -48,7 +48,7 @@ class CoroutineViewDebugSessionListener(
renew(suspendContext) renew(suspendContext)
} }
fun renew(suspendContext: XSuspendContext) { private fun renew(suspendContext: XSuspendContext) {
if (suspendContext is SuspendContextImpl) { if (suspendContext is SuspendContextImpl) {
DebuggerUIUtil.invokeLater { DebuggerUIUtil.invokeLater {
xCoroutineView.renewRoot(suspendContext) xCoroutineView.renewRoot(suspendContext)
@@ -8,7 +8,6 @@ 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.icons.AllIcons.General.Information
import com.intellij.openapi.editor.HighlighterColors import com.intellij.openapi.editor.HighlighterColors
import com.intellij.openapi.editor.colors.TextAttributesKey import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.ui.ColoredTextContainer import com.intellij.ui.ColoredTextContainer
@@ -25,11 +24,11 @@ import org.jetbrains.kotlin.idea.debugger.coroutine.data.State
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import javax.swing.Icon import javax.swing.Icon
class SimpleColoredTextIcon(val icon: Icon?, val hasChildrens: Boolean) { class SimpleColoredTextIcon(val icon: Icon?, val hasChildren: Boolean) {
val texts = mutableListOf<String>() private val texts = mutableListOf<String>()
val textKeyAttributes = mutableListOf<TextAttributesKey>() private val textKeyAttributes = mutableListOf<TextAttributesKey>()
constructor(icon: Icon?, hasChildrens: Boolean, text: String) : this(icon, hasChildrens) { constructor(icon: Icon?, hasChildren: Boolean, text: String) : this(icon, hasChildren) {
append(text) append(text)
} }
@@ -43,14 +42,14 @@ class SimpleColoredTextIcon(val icon: Icon?, val hasChildrens: Boolean) {
textKeyAttributes.add(CoroutineDebuggerColors.VALUE_ATTRIBUTES) textKeyAttributes.add(CoroutineDebuggerColors.VALUE_ATTRIBUTES)
} }
fun appendToComponent(component: ColoredTextContainer) { private fun appendToComponent(component: ColoredTextContainer) {
val size: Int = texts.size val size: Int = texts.size
for (i in 0 until size) { for (i in 0 until size) {
val text: String = texts.get(i) val text: String = texts[i]
val attribute: TextAttributesKey = textKeyAttributes.get(i) val attribute: TextAttributesKey = textKeyAttributes[i]
val simpleTextAttrinbute = toSimpleTextAttribute(attribute) val simpleTextAttribute = toSimpleTextAttribute(attribute)
component.append(text, simpleTextAttrinbute) component.append(text, simpleTextAttribute)
} }
} }
@@ -90,7 +89,7 @@ class SimpleColoredTextIcon(val icon: Icon?, val hasChildrens: Boolean) {
interface CoroutineDebuggerColors { interface CoroutineDebuggerColors {
companion object { companion object {
val REGULAR_ATTRIBUTES = HighlighterColors.TEXT val REGULAR_ATTRIBUTES: TextAttributesKey = HighlighterColors.TEXT
val VALUE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("KOTLIN_COROUTINE_DEBUGGER_VALUE", HighlighterColors.TEXT) val VALUE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("KOTLIN_COROUTINE_DEBUGGER_VALUE", HighlighterColors.TEXT)
} }
} }
@@ -185,7 +184,7 @@ class SimpleColoredTextIconPresentationRenderer {
) )
fun renderErrorNode(error: String) = fun renderErrorNode(error: String) =
SimpleColoredTextIcon(AllIcons.Actions.Lightning,false, KotlinDebuggerCoroutinesBundle.message(error)) SimpleColoredTextIcon(AllIcons.Actions.Lightning, false, KotlinDebuggerCoroutinesBundle.message(error))
fun renderInfoNode(text: String) = fun renderInfoNode(text: String) =
SimpleColoredTextIcon(AllIcons.General.Information, false, KotlinDebuggerCoroutinesBundle.message(text)) SimpleColoredTextIcon(AllIcons.General.Information, false, KotlinDebuggerCoroutinesBundle.message(text))
@@ -32,16 +32,12 @@ 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.KotlinDebuggerCoroutinesBundle import org.jetbrains.kotlin.idea.debugger.coroutine.KotlinDebuggerCoroutinesBundle
import org.jetbrains.kotlin.idea.debugger.coroutine.VersionedImplementationProvider import org.jetbrains.kotlin.idea.debugger.coroutine.VersionedImplementationProvider
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CoroutineFrameBuilder
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.CoroutineStackFrameItem import org.jetbrains.kotlin.idea.debugger.coroutine.data.CoroutineStackFrameItem
import org.jetbrains.kotlin.idea.debugger.coroutine.data.CreationCoroutineStackFrameItem import org.jetbrains.kotlin.idea.debugger.coroutine.data.CreationCoroutineStackFrameItem
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.CoroutineDebugProbesProxy import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.CoroutineDebugProbesProxy
import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ManagerThreadExecutor import org.jetbrains.kotlin.idea.debugger.coroutine.proxy.ManagerThreadExecutor
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CreateContentParams import org.jetbrains.kotlin.idea.debugger.coroutine.util.*
import org.jetbrains.kotlin.idea.debugger.coroutine.util.CreateContentParamsProvider
import org.jetbrains.kotlin.idea.debugger.coroutine.util.XDebugSessionListenerProvider
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import java.awt.BorderLayout import java.awt.BorderLayout
import javax.swing.JPanel import javax.swing.JPanel
@@ -49,20 +45,20 @@ 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 versionedImplementationProvider = VersionedImplementationProvider() private val versionedImplementationProvider = VersionedImplementationProvider()
val mainPanel = JPanel(BorderLayout()) val mainPanel = JPanel(BorderLayout())
val someCombobox = ComboBox<String>() 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({ resetRoot() }, VIEW_CLEAR_DELAY, this)
val renderer = SimpleColoredTextIconPresentationRenderer() val renderer = SimpleColoredTextIconPresentationRenderer()
val managerThreadExecutor = ManagerThreadExecutor(session) val managerThreadExecutor = ManagerThreadExecutor(session)
var treeState: XDebuggerTreeState? = null private var treeState: XDebuggerTreeState? = null
private var restorer: XDebuggerTreeRestorer? = null private var restorer: XDebuggerTreeRestorer? = null
private var selectedNodeListener: XDebuggerTreeSelectedNodeListener? = null private var selectedNodeListener: XDebuggerTreeSelectedNodeListener? = null
companion object { companion object {
private val VIEW_CLEAR_DELAY = 100 //ms private const val VIEW_CLEAR_DELAY = 100 //ms
} }
init { init {
@@ -97,7 +93,7 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
fun saveState() { fun saveState() {
DebuggerUIUtil.invokeLater { DebuggerUIUtil.invokeLater {
if (!(panel.tree.root is EmptyNode)) { if (panel.tree.root !is EmptyNode) {
treeState = XDebuggerTreeState.saveState(panel.tree) treeState = XDebuggerTreeState.saveState(panel.tree)
} }
} }
@@ -124,10 +120,6 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
} }
} }
fun forceClear() {
alarm.cancel()
}
override fun debugSessionListener(session: XDebugSession) = override fun debugSessionListener(session: XDebugSession) =
CoroutineViewDebugSessionListener(session, this) CoroutineViewDebugSessionListener(session, this)
@@ -155,10 +147,10 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
val debugProbesProxy = CoroutineDebugProbesProxy(suspendContext) val debugProbesProxy = CoroutineDebugProbesProxy(suspendContext)
val emptyDispatcherName = KotlinDebuggerCoroutinesBundle.message("coroutine.view.dispatcher.empty") val emptyDispatcherName = KotlinDebuggerCoroutinesBundle.message("coroutine.view.dispatcher.empty")
var coroutineCache = debugProbesProxy.dumpCoroutines() val coroutineCache = debugProbesProxy.dumpCoroutines()
if (coroutineCache.isOk()) { if (coroutineCache.isOk()) {
val children = XValueChildrenList() val children = XValueChildrenList()
var groups = coroutineCache.cache.groupBy { it.key.dispatcher } val groups = coroutineCache.cache.groupBy { it.key.dispatcher }
for (dispatcher in groups.keys) { for (dispatcher in groups.keys) {
children.add(CoroutineContainer(suspendContext, dispatcher ?: emptyDispatcherName, groups[dispatcher])) children.add(CoroutineContainer(suspendContext, dispatcher ?: emptyDispatcherName, groups[dispatcher]))
} }
@@ -248,7 +240,7 @@ class XCoroutineView(val project: Project, val session: XDebugSession) :
} }
private fun applyRenderer(node: XValueNode, presentation: SimpleColoredTextIcon) = private fun applyRenderer(node: XValueNode, presentation: SimpleColoredTextIcon) =
node.setPresentation(presentation.icon, presentation.valuePresentation(), presentation.hasChildrens) node.setPresentation(presentation.icon, presentation.valuePresentation(), presentation.hasChildren)
open inner class RendererContainer(val presentation: SimpleColoredTextIcon) : XNamedValue(presentation.simpleString()) { open inner class RendererContainer(val presentation: SimpleColoredTextIcon) : XNamedValue(presentation.simpleString()) {
override fun computePresentation(node: XValueNode, place: XValuePlace) = override fun computePresentation(node: XValueNode, place: XValuePlace) =
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor
import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
@@ -47,3 +48,5 @@ abstract class AbstractPrimitiveRangeToInspection : AbstractKotlinInspection() {
} }
} }
} }
var q = 12