From d2c2a6c99f469cc577bab951de588663a7f4b5a0 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Fri, 28 Dec 2018 19:50:05 +0300 Subject: [PATCH] Debugger: Support inline functions in Variables view --- .../kotlin/idea/debugger/KotlinStackFrame.kt | 72 +++++++++++++++---- .../kotlin/idea/debugger/debuggerUtil.kt | 12 ---- 2 files changed, 57 insertions(+), 27 deletions(-) diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinStackFrame.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinStackFrame.kt index fe8558ec4d3..cc8befc9e14 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinStackFrame.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinStackFrame.kt @@ -26,6 +26,8 @@ import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl import com.intellij.debugger.ui.impl.watch.ThisDescriptorImpl import com.intellij.xdebugger.frame.XValue import com.intellij.xdebugger.frame.XValueChildrenList +import com.sun.jdi.ReferenceType +import com.sun.jdi.Type import com.sun.jdi.Value import org.jetbrains.kotlin.codegen.AsmUtil 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 variable = ExistingInstanceThis.find(children) ?: return - val thisLabel = thisObject.referenceType().name().substringAfterLast('.').substringAfterLast('$') - if (thisLabel.isEmpty() || thisLabel in existingThisLabels || thisLabel.all { it.isDigit() }) { + val thisLabel = generateThisLabel(thisObject.referenceType())?.takeIf { it !in existingThisLabels } + if (thisLabel == null) { variable.remove() return } + // add additional checks? variable.remapName(getThisName(thisLabel)) } @@ -181,39 +184,78 @@ class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDe } } + val inlineDepth = VariableFinder.getInlineDepth(allVisibleVariables) + val (thisVariables, otherVariables) = allVisibleVariables.asSequence() - .filter { !isHidden(it) } - .partition { it.name() == AsmUtil.THIS || it.name().startsWith(AsmUtil.LABELED_THIS_PARAMETER) } + .filter { !isHidden(it, inlineDepth) } + .partition { + it.name() == AsmUtil.THIS + || it.name().startsWith(AsmUtil.LABELED_THIS_PARAMETER) + || (VariableFinder.inlinedThisRegex.matches(it.name())) + } val (mainThis, otherThis) = thisVariables .sortedByDescending { it.variable } .let { it.firstOrNull() to it.drop(1) } val remappedMainThis = mainThis?.clone(AsmUtil.THIS, null) - val remappedOtherThis = otherThis.map { it.remapThisName() } - - return (listOfNotNull(remappedMainThis) + remappedOtherThis + otherVariables) - .sortedBy { it.variable } + val remappedOther = (otherThis + otherVariables).map { it.remapVariableNameIfNeeded() } + return (listOfNotNull(remappedMainThis) + remappedOther).sortedBy { it.variable } } - private fun isHidden(variable: LocalVariableProxyImpl): Boolean { + private fun isHidden(variable: LocalVariableProxyImpl, inlineDepth: Int): Boolean { val name = variable.name() return isFakeLocalVariableForInline(name) - || name.endsWith(INLINE_FUN_VAR_SUFFIX) - || name == CONTINUATION_PARAMETER_NAME.asString() + || VariableFinder.getInlineDepth(variable.name()) != inlineDepth + || name == CONTINUATION_VARIABLE_NAME } - private fun LocalVariableProxyImpl.remapThisName(): LocalVariableProxyImpl { + private fun LocalVariableProxyImpl.remapVariableNameIfNeeded(): LocalVariableProxyImpl { + val name = this.name().dropInlineSuffix() + return when { 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) } - this@remapThisName.name() == AsmUtil.RECEIVER_PARAMETER_NAME -> clone(AsmUtil.THIS + " (receiver)", null) - else -> this@remapThisName + name == AsmUtil.RECEIVER_PARAMETER_NAME -> clone(AsmUtil.THIS + " (receiver)", null) + 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 { return "$THIS_NAME (@$label)" } diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt index dadd4b8893a..a6377935611 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/debuggerUtil.kt @@ -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.INVOKE_SUSPEND_METHOD_NAME 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.idea.codeInsight.CodeInsightUtils 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 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( inlineArgument: KtFunction, location: Location,