From bcf1954860c318e1c14e568285d54d1e6a99c8fc Mon Sep 17 00:00:00 2001 From: Vladimir Ilmov Date: Sun, 19 Jul 2020 02:05:31 +0200 Subject: [PATCH] (CoroutineDebugger) added support for DebugCoroutineInfoImpl in kotlinx-coroutines-core:1.3.8 relates to #KT-40073 --- .../proxy/mirror/coroutinesDebugMirror.kt | 58 +++++++++++++++++-- .../proxy/mirror/coroutinesMirror.kt | 6 +- .../coroutine/proxy/mirror/delegate.kt | 44 ++++++++++++++ 3 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 idea/jvm-debugger/jvm-debugger-coroutine/src/org/jetbrains/kotlin/idea/debugger/coroutine/proxy/mirror/delegate.kt diff --git a/idea/jvm-debugger/jvm-debugger-coroutine/src/org/jetbrains/kotlin/idea/debugger/coroutine/proxy/mirror/coroutinesDebugMirror.kt b/idea/jvm-debugger/jvm-debugger-coroutine/src/org/jetbrains/kotlin/idea/debugger/coroutine/proxy/mirror/coroutinesDebugMirror.kt index 1391ab4eef8..0b363cec50e 100644 --- a/idea/jvm-debugger/jvm-debugger-coroutine/src/org/jetbrains/kotlin/idea/debugger/coroutine/proxy/mirror/coroutinesDebugMirror.kt +++ b/idea/jvm-debugger/jvm-debugger-coroutine/src/org/jetbrains/kotlin/idea/debugger/coroutine/proxy/mirror/coroutinesDebugMirror.kt @@ -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(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("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("lastObservedThread") + val _context by MethodDelegate("getContext") + val _state by FieldDelegate("_state") + val _lastObservedFrame by FieldDelegate("_lastObservedFrame") + val creationStackBottom by FieldDelegate("creationStackBottom") + val sequenceNumber by FieldDelegate("sequenceNumber") + + val getCreationStackTrace by MethodDelegate("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) } } diff --git a/idea/jvm-debugger/jvm-debugger-coroutine/src/org/jetbrains/kotlin/idea/debugger/coroutine/proxy/mirror/coroutinesMirror.kt b/idea/jvm-debugger/jvm-debugger-coroutine/src/org/jetbrains/kotlin/idea/debugger/coroutine/proxy/mirror/coroutinesMirror.kt index d1d44a5a3cf..dd899cddbae 100644 --- a/idea/jvm-debugger/jvm-debugger-coroutine/src/org/jetbrains/kotlin/idea/debugger/coroutine/proxy/mirror/coroutinesMirror.kt +++ b/idea/jvm-debugger/jvm-debugger-coroutine/src/org/jetbrains/kotlin/idea/debugger/coroutine/proxy/mirror/coroutinesMirror.kt @@ -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(val name: String, context: DefaultExecutionContext) { +abstract class BaseMirror(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) diff --git a/idea/jvm-debugger/jvm-debugger-coroutine/src/org/jetbrains/kotlin/idea/debugger/coroutine/proxy/mirror/delegate.kt b/idea/jvm-debugger/jvm-debugger-coroutine/src/org/jetbrains/kotlin/idea/debugger/coroutine/proxy/mirror/delegate.kt new file mode 100644 index 00000000000..ba8efbace53 --- /dev/null +++ b/idea/jvm-debugger/jvm-debugger-coroutine/src/org/jetbrains/kotlin/idea/debugger/coroutine/proxy/mirror/delegate.kt @@ -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(val name: String) : ReadOnlyProperty> { + override fun getValue(thisRef: ReferenceTypeProvider, property: KProperty<*>): MethodEvaluator { + return MethodEvaluator(thisRef.getCls().methodsByName(name).singleOrNull()) + } + + @Suppress("UNCHECKED_CAST") + class MethodEvaluator(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(val name: String) : ReadOnlyProperty> { + override fun getValue(thisRef: ReferenceTypeProvider, property: KProperty<*>): FieldEvaluator { + return FieldEvaluator(thisRef.getCls().fieldByName(name)) + } + + @Suppress("UNCHECKED_CAST") + class FieldEvaluator(val field: Field?) { + fun value(value: ObjectReference): T? = + field?.let { value.getValue(it) as T? } + } +}