Support new $this_<label> field naming convention in debugger

This commit is contained in:
Yan Zhulanow
2018-09-08 00:58:29 +03:00
parent 50858b6fa3
commit 64e85e8a0c
7 changed files with 62 additions and 13 deletions
@@ -94,7 +94,7 @@ public class AsmUtil {
public static final String THIS = "this";
private static final String LABELED_THIS_FIELD = THIS + "_";
public static final String LABELED_THIS_FIELD = THIS + "_";
public static final String CAPTURED_THIS_FIELD = "this$0";
@@ -69,7 +69,7 @@ internal const val DEFAULT_LAMBDA_FAKE_CALL = "$$\$DEFAULT_LAMBDA_FAKE_CALL$$$"
internal const val CAPTURED_FIELD_FOLD_PREFIX = "$$$"
private const val NON_LOCAL_RETURN = "$$$$\$NON_LOCAL_RETURN$$$$$"
private const val CAPTURED_FIELD_PREFIX = "$"
const val CAPTURED_FIELD_PREFIX = "$"
private const val NON_CAPTURED_FIELD_PREFIX = "$$"
private const val INLINE_MARKER_CLASS_NAME = "kotlin/jvm/internal/InlineMarker"
private const val INLINE_MARKER_BEFORE_METHOD_NAME = "beforeInlineCall"
@@ -86,7 +86,11 @@ class KotlinSourcePositionProvider: SourcePositionProvider() {
private fun computeSourcePosition(descriptor: FieldDescriptor, project: Project, context: DebuggerContextImpl, nearest: Boolean): SourcePosition? {
val fieldName = descriptor.field.name()
if (fieldName == AsmUtil.CAPTURED_THIS_FIELD || fieldName == AsmUtil.CAPTURED_RECEIVER_FIELD) {
if (fieldName == AsmUtil.CAPTURED_THIS_FIELD
|| fieldName == AsmUtil.CAPTURED_RECEIVER_FIELD
|| fieldName.startsWith(AsmUtil.LABELED_THIS_FIELD)
) {
return null
}
@@ -100,11 +100,11 @@ fun Location.safeMethod(): Method? {
return DebuggerUtilsEx.getMethod(this)
}
fun isInsideInlineFunctionBody(visibleVariables: List<LocalVariableProxyImpl>): Boolean {
fun isInsideInlineFunctionBody(visibleVariables: List<LocalVariable>): Boolean {
return visibleVariables.any { it.name().startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) }
}
fun numberOfInlinedFunctions(visibleVariables: List<LocalVariableProxyImpl>): Int {
fun numberOfInlinedFunctions(visibleVariables: List<LocalVariable>): Int {
return visibleVariables.count { it.name().startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) }
}
@@ -19,14 +19,17 @@ package org.jetbrains.kotlin.idea.debugger.evaluate
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.openapi.diagnostic.Attachment
import com.intellij.patterns.uast.capture
import com.sun.jdi.ClassType
import com.sun.jdi.InvalidStackFrameException
import com.sun.jdi.ObjectReference
import com.sun.jdi.StackFrame
import org.jetbrains.eval4j.Value
import org.jetbrains.eval4j.jdi.asJdiValue
import org.jetbrains.eval4j.jdi.asValue
import org.jetbrains.eval4j.obj
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.inline.CAPTURED_FIELD_PREFIX
import org.jetbrains.kotlin.codegen.inline.INLINE_FUN_VAR_SUFFIX
import org.jetbrains.kotlin.codegen.inline.INLINE_TRANSFORMATION_SUFFIX
import org.jetbrains.kotlin.codegen.inline.NUMBERED_FUNCTION_PREFIX
@@ -49,12 +52,12 @@ class FrameVisitor(val context: EvaluationContextImpl) {
}
fun findValue(name: String, asmType: Type?, checkType: Boolean, failIfNotFound: Boolean): Value? {
if (frame == null) return null
val frame = this.frame?.stackFrame ?: return null
try {
when (name) {
THIS_NAME -> {
val thisValue = findThis(asmType)
val thisValue = findThis(frame, asmType)
if (thisValue != null) {
return thisValue
}
@@ -125,8 +128,8 @@ class FrameVisitor(val context: EvaluationContextImpl) {
throw EvaluateExceptionUtil.createEvaluateException(message)
}
private fun findThis(asmType: Type?): Value? {
if (isInsideInlineFunctionBody(frame!!.visibleVariables())) {
private fun findThis(frame: StackFrame, asmType: Type?): Value? {
if (isInsideInlineFunctionBody(frame.visibleVariables())) {
val number = numberOfInlinedFunctions(frame.visibleVariables())
val inlineFunVar = findLocalVariableForInlineArgument("this_", number, asmType, true)
if (inlineFunVar != null) {
@@ -134,18 +137,34 @@ class FrameVisitor(val context: EvaluationContextImpl) {
}
}
// Captured labeled 'this'
frame.visibleVariables()
.asSequence()
.filter { it.name().startsWith(AsmUtil.LABELED_THIS_FIELD) }
.map { it to frame.getValue(it).asValue() }
.filter { isValueOfCorrectType(it.second, asmType, true) }
.maxBy { it.first }
?.let { return frame.getValue(it.first).asValue() }
val thisObject = frame.thisObject()
if (thisObject != null) {
val eval4jValue = thisObject.asValue()
if (isValueOfCorrectType(eval4jValue, asmType, true)) return eval4jValue
if (isValueOfCorrectType(eval4jValue, asmType, true)) {
return eval4jValue
}
findThisInCapturedThis(thisObject, asmType)?.let { return it }
}
// TODO this is probably not needed anymore (covered by 'findThisInCapturedThis')
val receiver = findValue(RECEIVER_NAME, asmType, checkType = true, failIfNotFound = false)
if (receiver != null) return receiver
// TODO this is probably not needed anymore (covered by 'findThisInCapturedThis')
val this0 = findValue(AsmUtil.CAPTURED_THIS_FIELD, asmType, checkType = true, failIfNotFound = false)
if (this0 != null) return this0
// TODO this is probably not needed anymore (used only in JS)
val `$this` = findValue("\$this", asmType, checkType = false, failIfNotFound = false)
if (`$this` != null) return `$this`
@@ -160,6 +179,32 @@ class FrameVisitor(val context: EvaluationContextImpl) {
return findLocalVariable(name + INLINE_FUN_VAR_SUFFIX.repeat(number), asmType, checkType)
}
private fun findThisInCapturedThis(capturedThis: ObjectReference, asmType: Type?): Value? {
for (field in capturedThis.referenceType().fields()) {
val name = field.name()
if (name == AsmUtil.CAPTURED_THIS_FIELD) {
val value = capturedThis.getValue(field)
if (value is ObjectReference) {
findThisInCapturedThis(value, asmType)?.let { return it }
}
}
if (name.startsWith(CAPTURED_FIELD_PREFIX + AsmUtil.LABELED_THIS_FIELD)
|| name == AsmUtil.CAPTURED_RECEIVER_FIELD
|| name == AsmUtil.CAPTURED_THIS_FIELD
) {
val value = capturedThis.getValue(field)
val evalValue = value.asValue()
if (isValueOfCorrectType(evalValue, asmType, true)) {
return evalValue
}
}
}
return null
}
private fun isFunctionType(type: Type?): Boolean {
return type?.sort == Type.OBJECT &&
type.internalName.startsWith(NUMBERED_FUNCTION_PREFIX)
@@ -89,10 +89,10 @@ fun lambda(f: () -> Unit) {
field = lcProp: int = 1 (sp = frameExtFunExtFun.kt, 19)
field = this$0: frameExtFunExtFun.Outer = {frameExtFunExtFun.Outer@uniqueID} (sp = null)
field = outerProp: int = 1 (sp = frameExtFunExtFun.kt, 13)
field = receiver$0: frameExtFunExtFun.A = {frameExtFunExtFun.A@uniqueID} (sp = null)
field = $this_foo: frameExtFunExtFun.A = {frameExtFunExtFun.A@uniqueID} (sp = null)
field = aProp: int = 1 (sp = frameExtFunExtFun.kt, 8)
field = $valFoo: int = 1 (sp = null)
field = receiver$0: frameExtFunExtFun.B = {frameExtFunExtFun.B@uniqueID} (sp = null)
field = $this_test: frameExtFunExtFun.B = {frameExtFunExtFun.B@uniqueID} (sp = null)
field = bProp: int = 1 (sp = frameExtFunExtFun.kt, 41)
field = $valTest: int = 1 (sp = null)
field = arity: int = 0 (sp = Lambda.!EXT!)
@@ -67,7 +67,7 @@ fun foo(f: () -> Unit) {
this = this = {frameThis0Ext.A$testExt$1@uniqueID}Function0<kotlin.Unit>
field = this$0: frameThis0Ext.A = {frameThis0Ext.A@uniqueID} (sp = null)
field = prop1: int = 1 (sp = frameThis0Ext.kt, 8)
field = receiver$0: frameThis0Ext.AExt = {frameThis0Ext.AExt@uniqueID} (sp = null)
field = $this_testExt: frameThis0Ext.AExt = {frameThis0Ext.AExt@uniqueID} (sp = null)
field = prop2: int = 1 (sp = frameThis0Ext.kt, 25)
field = $val1: int = 1 (sp = null)
field = arity: int = 0 (sp = Lambda.!EXT!)