Debugger: Support inline functions in Variables view
This commit is contained in:
@@ -26,6 +26,8 @@ import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
|
|||||||
import com.intellij.debugger.ui.impl.watch.ThisDescriptorImpl
|
import com.intellij.debugger.ui.impl.watch.ThisDescriptorImpl
|
||||||
import com.intellij.xdebugger.frame.XValue
|
import com.intellij.xdebugger.frame.XValue
|
||||||
import com.intellij.xdebugger.frame.XValueChildrenList
|
import com.intellij.xdebugger.frame.XValueChildrenList
|
||||||
|
import com.sun.jdi.ReferenceType
|
||||||
|
import com.sun.jdi.Type
|
||||||
import com.sun.jdi.Value
|
import com.sun.jdi.Value
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_PARAMETER_NAME
|
import org.jetbrains.kotlin.codegen.coroutines.CONTINUATION_PARAMETER_NAME
|
||||||
@@ -86,12 +88,13 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe
|
|||||||
val thisObject = evaluationContext.frameProxy?.thisObject() ?: return
|
val thisObject = evaluationContext.frameProxy?.thisObject() ?: return
|
||||||
val variable = ExistingInstanceThis.find(children) ?: return
|
val variable = ExistingInstanceThis.find(children) ?: return
|
||||||
|
|
||||||
val thisLabel = thisObject.referenceType().name().substringAfterLast('.').substringAfterLast('$')
|
val thisLabel = generateThisLabel(thisObject.referenceType())?.takeIf { it !in existingThisLabels }
|
||||||
if (thisLabel.isEmpty() || thisLabel in existingThisLabels || thisLabel.all { it.isDigit() }) {
|
if (thisLabel == null) {
|
||||||
variable.remove()
|
variable.remove()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add additional checks?
|
||||||
variable.remapName(getThisName(thisLabel))
|
variable.remapName(getThisName(thisLabel))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,39 +184,78 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val inlineDepth = VariableFinder.getInlineDepth(allVisibleVariables)
|
||||||
|
|
||||||
val (thisVariables, otherVariables) = allVisibleVariables.asSequence()
|
val (thisVariables, otherVariables) = allVisibleVariables.asSequence()
|
||||||
.filter { !isHidden(it) }
|
.filter { !isHidden(it, inlineDepth) }
|
||||||
.partition { it.name() == AsmUtil.THIS || it.name().startsWith(AsmUtil.LABELED_THIS_PARAMETER) }
|
.partition {
|
||||||
|
it.name() == AsmUtil.THIS
|
||||||
|
|| it.name().startsWith(AsmUtil.LABELED_THIS_PARAMETER)
|
||||||
|
|| (VariableFinder.inlinedThisRegex.matches(it.name()))
|
||||||
|
}
|
||||||
|
|
||||||
val (mainThis, otherThis) = thisVariables
|
val (mainThis, otherThis) = thisVariables
|
||||||
.sortedByDescending { it.variable }
|
.sortedByDescending { it.variable }
|
||||||
.let { it.firstOrNull() to it.drop(1) }
|
.let { it.firstOrNull() to it.drop(1) }
|
||||||
|
|
||||||
val remappedMainThis = mainThis?.clone(AsmUtil.THIS, null)
|
val remappedMainThis = mainThis?.clone(AsmUtil.THIS, null)
|
||||||
val remappedOtherThis = otherThis.map { it.remapThisName() }
|
val remappedOther = (otherThis + otherVariables).map { it.remapVariableNameIfNeeded() }
|
||||||
|
return (listOfNotNull(remappedMainThis) + remappedOther).sortedBy { it.variable }
|
||||||
return (listOfNotNull(remappedMainThis) + remappedOtherThis + otherVariables)
|
|
||||||
.sortedBy { it.variable }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isHidden(variable: LocalVariableProxyImpl): Boolean {
|
private fun isHidden(variable: LocalVariableProxyImpl, inlineDepth: Int): Boolean {
|
||||||
val name = variable.name()
|
val name = variable.name()
|
||||||
return isFakeLocalVariableForInline(name)
|
return isFakeLocalVariableForInline(name)
|
||||||
|| name.endsWith(INLINE_FUN_VAR_SUFFIX)
|
|| VariableFinder.getInlineDepth(variable.name()) != inlineDepth
|
||||||
|| name == CONTINUATION_PARAMETER_NAME.asString()
|
|| name == CONTINUATION_VARIABLE_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LocalVariableProxyImpl.remapThisName(): LocalVariableProxyImpl {
|
private fun LocalVariableProxyImpl.remapVariableNameIfNeeded(): LocalVariableProxyImpl {
|
||||||
|
val name = this.name().dropInlineSuffix()
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
isLabeledThisReference() -> {
|
isLabeledThisReference() -> {
|
||||||
val label = this@remapThisName.name().drop(AsmUtil.LABELED_THIS_PARAMETER.length)
|
val label = name.drop(AsmUtil.LABELED_THIS_PARAMETER.length)
|
||||||
clone(getThisName(label), label)
|
clone(getThisName(label), label)
|
||||||
}
|
}
|
||||||
this@remapThisName.name() == AsmUtil.RECEIVER_PARAMETER_NAME -> clone(AsmUtil.THIS + " (receiver)", null)
|
name == AsmUtil.RECEIVER_PARAMETER_NAME -> clone(AsmUtil.THIS + " (receiver)", null)
|
||||||
else -> this@remapThisName
|
VariableFinder.inlinedThisRegex.matches(name) -> {
|
||||||
|
val label = generateThisLabel(frame.getValue(this)?.type())
|
||||||
|
if (label != null) {
|
||||||
|
clone(getThisName(label), label)
|
||||||
|
} else {
|
||||||
|
this@remapVariableNameIfNeeded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name != this.name() -> {
|
||||||
|
object : LocalVariableProxyImpl(frame, variable) {
|
||||||
|
override fun name() = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> this@remapVariableNameIfNeeded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun generateThisLabel(type: Type?): String? {
|
||||||
|
val referenceType = type as? ReferenceType ?: return null
|
||||||
|
val label = referenceType.name().substringAfterLast('.').substringAfterLast('$')
|
||||||
|
|
||||||
|
if (label.isEmpty() || label.any { it.isDigit() }) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.dropInlineSuffix(): String {
|
||||||
|
val depth = VariableFinder.getInlineDepth(this)
|
||||||
|
if (depth == 0) {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
return dropLast(depth * INLINE_FUN_VAR_SUFFIX.length)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getThisName(label: String): String {
|
private fun getThisName(label: String): String {
|
||||||
return "$THIS_NAME (@$label)"
|
return "$THIS_NAME (@$label)"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.codegen.binding.CodegenBinding.asmTypeForAnonymousCl
|
|||||||
import org.jetbrains.kotlin.codegen.coroutines.DO_RESUME_METHOD_NAME
|
import org.jetbrains.kotlin.codegen.coroutines.DO_RESUME_METHOD_NAME
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME
|
import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.continuationAsmTypes
|
import org.jetbrains.kotlin.codegen.coroutines.continuationAsmTypes
|
||||||
import org.jetbrains.kotlin.codegen.inline.INLINE_FUN_VAR_SUFFIX
|
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches
|
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches
|
||||||
@@ -33,17 +32,6 @@ import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
|||||||
import org.jetbrains.org.objectweb.asm.Type as AsmType
|
import org.jetbrains.org.objectweb.asm.Type as AsmType
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
fun calculateInlineDepth(variableName: String): Int {
|
|
||||||
var lastIndex = variableName.lastIndex
|
|
||||||
var depth = 0
|
|
||||||
|
|
||||||
while (variableName.lastIndexOf(INLINE_FUN_VAR_SUFFIX, startIndex = lastIndex) >= 0) {
|
|
||||||
lastIndex -= INLINE_FUN_VAR_SUFFIX.length
|
|
||||||
depth++
|
|
||||||
}
|
|
||||||
return depth
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isInsideInlineArgument(
|
fun isInsideInlineArgument(
|
||||||
inlineArgument: KtFunction,
|
inlineArgument: KtFunction,
|
||||||
location: Location,
|
location: Location,
|
||||||
|
|||||||
Reference in New Issue
Block a user