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"; 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"; 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 = "$$$" internal const val CAPTURED_FIELD_FOLD_PREFIX = "$$$"
private const val NON_LOCAL_RETURN = "$$$$\$NON_LOCAL_RETURN$$$$$" 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 NON_CAPTURED_FIELD_PREFIX = "$$"
private const val INLINE_MARKER_CLASS_NAME = "kotlin/jvm/internal/InlineMarker" private const val INLINE_MARKER_CLASS_NAME = "kotlin/jvm/internal/InlineMarker"
private const val INLINE_MARKER_BEFORE_METHOD_NAME = "beforeInlineCall" 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? { private fun computeSourcePosition(descriptor: FieldDescriptor, project: Project, context: DebuggerContextImpl, nearest: Boolean): SourcePosition? {
val fieldName = descriptor.field.name() 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 return null
} }
@@ -100,11 +100,11 @@ fun Location.safeMethod(): Method? {
return DebuggerUtilsEx.getMethod(this) 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) } 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) } 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.EvaluateExceptionUtil
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.openapi.diagnostic.Attachment import com.intellij.openapi.diagnostic.Attachment
import com.intellij.patterns.uast.capture
import com.sun.jdi.ClassType import com.sun.jdi.ClassType
import com.sun.jdi.InvalidStackFrameException import com.sun.jdi.InvalidStackFrameException
import com.sun.jdi.ObjectReference import com.sun.jdi.ObjectReference
import com.sun.jdi.StackFrame
import org.jetbrains.eval4j.Value import org.jetbrains.eval4j.Value
import org.jetbrains.eval4j.jdi.asJdiValue import org.jetbrains.eval4j.jdi.asJdiValue
import org.jetbrains.eval4j.jdi.asValue import org.jetbrains.eval4j.jdi.asValue
import org.jetbrains.eval4j.obj import org.jetbrains.eval4j.obj
import org.jetbrains.kotlin.codegen.AsmUtil 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_FUN_VAR_SUFFIX
import org.jetbrains.kotlin.codegen.inline.INLINE_TRANSFORMATION_SUFFIX import org.jetbrains.kotlin.codegen.inline.INLINE_TRANSFORMATION_SUFFIX
import org.jetbrains.kotlin.codegen.inline.NUMBERED_FUNCTION_PREFIX 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? { fun findValue(name: String, asmType: Type?, checkType: Boolean, failIfNotFound: Boolean): Value? {
if (frame == null) return null val frame = this.frame?.stackFrame ?: return null
try { try {
when (name) { when (name) {
THIS_NAME -> { THIS_NAME -> {
val thisValue = findThis(asmType) val thisValue = findThis(frame, asmType)
if (thisValue != null) { if (thisValue != null) {
return thisValue return thisValue
} }
@@ -125,8 +128,8 @@ class FrameVisitor(val context: EvaluationContextImpl) {
throw EvaluateExceptionUtil.createEvaluateException(message) throw EvaluateExceptionUtil.createEvaluateException(message)
} }
private fun findThis(asmType: Type?): Value? { private fun findThis(frame: StackFrame, asmType: Type?): Value? {
if (isInsideInlineFunctionBody(frame!!.visibleVariables())) { if (isInsideInlineFunctionBody(frame.visibleVariables())) {
val number = numberOfInlinedFunctions(frame.visibleVariables()) val number = numberOfInlinedFunctions(frame.visibleVariables())
val inlineFunVar = findLocalVariableForInlineArgument("this_", number, asmType, true) val inlineFunVar = findLocalVariableForInlineArgument("this_", number, asmType, true)
if (inlineFunVar != null) { 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() val thisObject = frame.thisObject()
if (thisObject != null) { if (thisObject != null) {
val eval4jValue = thisObject.asValue() 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) val receiver = findValue(RECEIVER_NAME, asmType, checkType = true, failIfNotFound = false)
if (receiver != null) return receiver 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) val this0 = findValue(AsmUtil.CAPTURED_THIS_FIELD, asmType, checkType = true, failIfNotFound = false)
if (this0 != null) return this0 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) val `$this` = findValue("\$this", asmType, checkType = false, failIfNotFound = false)
if (`$this` != null) return `$this` 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) 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 { private fun isFunctionType(type: Type?): Boolean {
return type?.sort == Type.OBJECT && return type?.sort == Type.OBJECT &&
type.internalName.startsWith(NUMBERED_FUNCTION_PREFIX) type.internalName.startsWith(NUMBERED_FUNCTION_PREFIX)
@@ -89,10 +89,10 @@ fun lambda(f: () -> Unit) {
field = lcProp: int = 1 (sp = frameExtFunExtFun.kt, 19) field = lcProp: int = 1 (sp = frameExtFunExtFun.kt, 19)
field = this$0: frameExtFunExtFun.Outer = {frameExtFunExtFun.Outer@uniqueID} (sp = null) field = this$0: frameExtFunExtFun.Outer = {frameExtFunExtFun.Outer@uniqueID} (sp = null)
field = outerProp: int = 1 (sp = frameExtFunExtFun.kt, 13) 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 = aProp: int = 1 (sp = frameExtFunExtFun.kt, 8)
field = $valFoo: int = 1 (sp = null) 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 = bProp: int = 1 (sp = frameExtFunExtFun.kt, 41)
field = $valTest: int = 1 (sp = null) field = $valTest: int = 1 (sp = null)
field = arity: int = 0 (sp = Lambda.!EXT!) 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> this = this = {frameThis0Ext.A$testExt$1@uniqueID}Function0<kotlin.Unit>
field = this$0: frameThis0Ext.A = {frameThis0Ext.A@uniqueID} (sp = null) field = this$0: frameThis0Ext.A = {frameThis0Ext.A@uniqueID} (sp = null)
field = prop1: int = 1 (sp = frameThis0Ext.kt, 8) 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 = prop2: int = 1 (sp = frameThis0Ext.kt, 25)
field = $val1: int = 1 (sp = null) field = $val1: int = 1 (sp = null)
field = arity: int = 0 (sp = Lambda.!EXT!) field = arity: int = 0 (sp = Lambda.!EXT!)