Support new $this_<label> field naming convention in debugger
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
+5
-1
@@ -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)
|
||||||
|
|||||||
+2
-2
@@ -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!)
|
||||||
|
|||||||
+1
-1
@@ -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!)
|
||||||
|
|||||||
Reference in New Issue
Block a user