(CoroutineDebugger) added support for DebugCoroutineInfoImpl in kotlinx-coroutines-core:1.3.8

relates to #KT-40073
This commit is contained in:
Vladimir Ilmov
2020-07-19 02:05:31 +02:00
parent b43b238030
commit bcf1954860
3 changed files with 100 additions and 8 deletions
@@ -5,8 +5,7 @@
package org.jetbrains.kotlin.idea.debugger.coroutine.proxy.mirror
import com.sun.jdi.ObjectReference
import com.sun.jdi.ThreadReference
import com.sun.jdi.*
import org.jetbrains.kotlin.idea.debugger.coroutine.util.isSubTypeOrSame
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
@@ -67,11 +66,16 @@ class DebugProbesImpl private constructor(context: DefaultExecutionContext) :
class DebugProbesImplCoroutineOwner(private val coroutineInfo: CoroutineInfo, context: DefaultExecutionContext) :
BaseMirror<MirrorOfCoroutineOwner>(COROUTINE_OWNER_CLASS_NAME, context) {
private val debugCoroutineInfoImpl = DebugCoroutineInfoImpl(context)
private val infoField = makeField("info")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfCoroutineOwner? {
val info = objectValue(value, infoField)
return MirrorOfCoroutineOwner(value, coroutineInfo.mirror(info, context))
if (debugCoroutineInfoImpl.isCompatible(info)) {
return MirrorOfCoroutineOwner(value, debugCoroutineInfoImpl.mirror(info, context))
} else
return MirrorOfCoroutineOwner(value, coroutineInfo.mirror(info, context))
}
companion object {
@@ -86,6 +90,47 @@ data class MirrorOfCoroutineOwner(val that: ObjectReference, val coroutineInfo:
data class MirrorOfDebugProbesImpl(val that: ObjectReference, val instance: ObjectReference?, val isInstalled: Boolean?)
class DebugCoroutineInfoImpl constructor(context: DefaultExecutionContext) :
BaseMirror<MirrorOfCoroutineInfo>("kotlinx.coroutines.debug.internal.DebugCoroutineInfoImpl", context) {
private val coroutineContextMirror = CoroutineContext(context)
private val javaLangMirror = JavaLangMirror(context)
private val javaLangListMirror = JavaUtilAbstractCollection(context)
private val stackTraceElement = StackTraceElement(context)
val lastObservedThread by FieldDelegate<ThreadReference>("lastObservedThread")
val _context by MethodDelegate<ObjectReference>("getContext")
val _state by FieldDelegate<ObjectReference>("_state")
val _lastObservedFrame by FieldDelegate<ObjectReference>("_lastObservedFrame")
val creationStackBottom by FieldDelegate<ObjectReference>("creationStackBottom")
val sequenceNumber by FieldDelegate<LongValue>("sequenceNumber")
val getCreationStackTrace by MethodDelegate<ObjectReference>("getCreationStackTrace")
override fun fetchMirror(value: ObjectReference, context: DefaultExecutionContext): MirrorOfCoroutineInfo? {
val state = _state.value(value)?.let {
stringValue(it, javaLangMirror.toString, context)
}
val coroutineContext = coroutineContextMirror.mirror(_context.value(value, context), context)
val creationStackBottom = creationStackBottom.value(value)?.let { CoroutineStackFrame(it, context).mirror() }
val creationStackTraceMirror = javaLangListMirror.mirror(getCreationStackTrace.value(value, context), context)
val creationStackTrace = creationStackTraceMirror?.values?.mapNotNull { stackTraceElement.mirror(it, context) }
return MirrorOfCoroutineInfo(
value,
coroutineContext,
creationStackBottom,
sequenceNumber.value(value)?.longValue(),
null,
creationStackTrace,
state,
lastObservedThread.value(value),
_lastObservedFrame.value(value)
)
}
}
class CoroutineInfo private constructor(
private val debugProbesImplMirror: DebugProbesImpl,
context: DefaultExecutionContext,
@@ -156,7 +201,6 @@ class CoroutineInfo private constructor(
}
}
data class MirrorOfCoroutineInfo(
val that: ObjectReference,
val context: MirrorOfCoroutineContext?,
@@ -180,8 +224,10 @@ class CoroutineStackFrame(value: ObjectReference, context: DefaultExecutionConte
val callerFrame = if (objectReference is ObjectReference)
CoroutineStackFrame(objectReference, context).mirror() else null
val stackTraceElementReference = objectValue(value, getStackTraceElementMethod, context)
val stackTraceElement =
if (stackTraceElementReference is ObjectReference) stackTraceElementMirror.mirror(stackTraceElementReference, context) else null
val stackTraceElement = if (stackTraceElementReference is ObjectReference)
stackTraceElementMirror.mirror(stackTraceElementReference, context)
else
null
return MirrorOfCoroutineStackFrame(value, callerFrame, stackTraceElement)
}
}
@@ -10,9 +10,11 @@ import org.jetbrains.kotlin.idea.debugger.coroutine.util.isSubTypeOrSame
import org.jetbrains.kotlin.idea.debugger.coroutine.util.logger
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
abstract class BaseMirror<T>(val name: String, context: DefaultExecutionContext) {
abstract class BaseMirror<T>(val name: String, context: DefaultExecutionContext) : ReferenceTypeProvider {
val log by logger
protected val cls = context.findClassSafe(name) ?: throw IllegalStateException("coroutine-debugger: class $name not found.")
private val cls = context.findClassSafe(name) ?: throw IllegalStateException("coroutine-debugger: class $name not found.")
override fun getCls(): ClassType = cls
fun makeField(fieldName: String): Field? =
cls.fieldByName(fieldName)
@@ -0,0 +1,44 @@
/*
* 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.mirror
import com.sun.jdi.*
import org.jetbrains.kotlin.idea.debugger.evaluate.DefaultExecutionContext
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
interface ReferenceTypeProvider {
fun getCls(): ClassType
}
class MethodDelegate<T>(val name: String) : ReadOnlyProperty<ReferenceTypeProvider, MethodDelegate.MethodEvaluator<T>> {
override fun getValue(thisRef: ReferenceTypeProvider, property: KProperty<*>): MethodEvaluator<T> {
return MethodEvaluator(thisRef.getCls().methodsByName(name).singleOrNull())
}
@Suppress("UNCHECKED_CAST")
class MethodEvaluator<T>(val method: Method?) {
fun value(value: ObjectReference?, context: DefaultExecutionContext, vararg values: Value): T? {
return value?.let {
method?.let {
context.invokeMethodAsObject(value, method, *values) as T?
}
}
}
}
}
class FieldDelegate<T>(val name: String) : ReadOnlyProperty<ReferenceTypeProvider, FieldDelegate.FieldEvaluator<T>> {
override fun getValue(thisRef: ReferenceTypeProvider, property: KProperty<*>): FieldEvaluator<T> {
return FieldEvaluator(thisRef.getCls().fieldByName(name))
}
@Suppress("UNCHECKED_CAST")
class FieldEvaluator<T>(val field: Field?) {
fun value(value: ObjectReference): T? =
field?.let { value.getValue(it) as T? }
}
}