Debugger: add an option for computing delegated properties values in Variables View
This commit is contained in:
@@ -480,3 +480,4 @@ kotlin.compiler.js.option.output.copy.dir=O&utput directory for library &runtime
|
||||
|
||||
# Debugger
|
||||
debugger.filter.ignore.internal.classes=Do not step into specific Kotlin classes
|
||||
debugger.data.view.delegated.properties=Calculate values of delegated properties (may affect program execution)
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.intellij.xdebugger.XDebuggerUtil
|
||||
|
||||
State(name = "KotlinDebuggerSettings", storages = array(Storage(file = StoragePathMacros.APP_CONFIG + "/kotlin_debug.xml")))
|
||||
public class KotlinDebuggerSettings : XDebuggerSettings<KotlinDebuggerSettings>("kotlin_debugger"), Getter<KotlinDebuggerSettings> {
|
||||
public var DEBUG_RENDER_DELEGATED_PROPERTIES: Boolean = true
|
||||
public var DEBUG_DISABLE_KOTLIN_INTERNAL_CLASSES: Boolean = true
|
||||
public var DEBUG_IS_FILTER_FOR_STDLIB_ALREADY_ADDED: Boolean = false
|
||||
|
||||
@@ -44,10 +45,16 @@ public class KotlinDebuggerSettings : XDebuggerSettings<KotlinDebuggerSettings>(
|
||||
return when (category) {
|
||||
DebuggerSettingsCategory.STEPPING ->
|
||||
listOf(SimpleConfigurable.create(
|
||||
"reference.idesettings.debugger.kotlin",
|
||||
"reference.idesettings.debugger.kotlin.stepping",
|
||||
"Kotlin",
|
||||
javaClass<KotlinSteppingConfigurableUi>(),
|
||||
this))
|
||||
DebuggerSettingsCategory.DATA_VIEWS ->
|
||||
listOf(SimpleConfigurable.create(
|
||||
"reference.idesettings.debugger.kotlin.data.view",
|
||||
"Kotlin",
|
||||
javaClass<KotlinDelegatedPropertyRendererConfigurableUi>(),
|
||||
this))
|
||||
else -> listOf()
|
||||
}
|
||||
}
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.kotlin.idea.debugger.KotlinDelegatedPropertyRendererConfigurableUi">
|
||||
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="500" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="99d36" class="javax.swing.JCheckBox" binding="renderDelegatedProperties">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<selected value="false"/>
|
||||
<text resource-bundle="org/jetbrains/kotlin/idea/JetBundle" key="debugger.data.view.delegated.properties"/>
|
||||
</properties>
|
||||
</component>
|
||||
<vspacer id="c37da">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger;
|
||||
|
||||
|
||||
import com.intellij.openapi.options.ConfigurableUi;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class KotlinDelegatedPropertyRendererConfigurableUi implements ConfigurableUi<KotlinDebuggerSettings> {
|
||||
private JCheckBox renderDelegatedProperties;
|
||||
private JPanel myPanel;
|
||||
|
||||
@Override
|
||||
public void reset(@NotNull KotlinDebuggerSettings settings) {
|
||||
boolean flag = settings.getDEBUG_RENDER_DELEGATED_PROPERTIES();
|
||||
renderDelegatedProperties.setSelected(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModified(@NotNull KotlinDebuggerSettings settings) {
|
||||
return settings.getDEBUG_RENDER_DELEGATED_PROPERTIES() != renderDelegatedProperties.isSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(@NotNull KotlinDebuggerSettings settings) {
|
||||
settings.setDEBUG_RENDER_DELEGATED_PROPERTIES(renderDelegatedProperties.isSelected());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return myPanel;
|
||||
}
|
||||
}
|
||||
+41
-15
@@ -16,29 +16,50 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.render
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.sun.jdi.Field
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||
import com.sun.jdi.Value
|
||||
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl
|
||||
import com.intellij.debugger.DebuggerContext
|
||||
import com.intellij.psi.PsiExpression
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||
import com.intellij.debugger.settings.NodeRendererSettings
|
||||
import com.intellij.debugger.ui.tree.FieldDescriptor
|
||||
import com.intellij.debugger.ui.impl.watch.FieldDescriptorImpl
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiExpression
|
||||
import com.sun.jdi.Field
|
||||
import com.sun.jdi.Method
|
||||
import com.sun.jdi.ObjectReference
|
||||
import com.sun.jdi.Value
|
||||
import org.jetbrains.kotlin.codegen.PropertyCodegen
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class DelegatedPropertyFieldDescriptor(
|
||||
project: Project,
|
||||
val computedValueFromGetter: Value,
|
||||
val objectRef: ObjectReference,
|
||||
val delegate: Field
|
||||
): ValueDescriptorImpl(project, computedValueFromGetter), FieldDescriptor {
|
||||
override fun getField() = delegate
|
||||
override fun getObject() = objectRef
|
||||
val delegate: Field,
|
||||
val renderDelegatedProperty: Boolean
|
||||
) : FieldDescriptorImpl(project, objectRef, delegate) {
|
||||
|
||||
override fun calcValue(evaluationContext: EvaluationContextImpl?): Value? {
|
||||
return computedValueFromGetter
|
||||
if (evaluationContext == null) return null
|
||||
if (!renderDelegatedProperty) return super.calcValue(evaluationContext)
|
||||
|
||||
val method = findGetterForDelegatedProperty()
|
||||
val threadReference = evaluationContext.getSuspendContext().getThread()?.getThreadReference()
|
||||
if (method == null || threadReference == null) {
|
||||
return super.calcValue(evaluationContext)
|
||||
}
|
||||
|
||||
try {
|
||||
return evaluationContext.getDebugProcess().invokeInstanceMethod(
|
||||
evaluationContext,
|
||||
getObject(),
|
||||
method,
|
||||
listOf<Nothing>(),
|
||||
evaluationContext.getSuspendContext().getSuspendPolicy()
|
||||
)
|
||||
}
|
||||
catch(e: EvaluateException) {
|
||||
return e.getExceptionFromTargetVM()
|
||||
}
|
||||
}
|
||||
|
||||
override fun calcValueName(): String? {
|
||||
@@ -54,12 +75,17 @@ class DelegatedPropertyFieldDescriptor(
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return delegate.name().trimTrailing(JvmAbi.DELEGATED_PROPERTY_NAME_SUFFIX)
|
||||
return delegate.name().removeSuffix(JvmAbi.DELEGATED_PROPERTY_NAME_SUFFIX)
|
||||
}
|
||||
|
||||
override fun getDescriptorEvaluation(context: DebuggerContext?): PsiExpression? {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun findGetterForDelegatedProperty(): Method? {
|
||||
val fieldName = getName()
|
||||
val getterName = PropertyCodegen.getterName(Name.identifier(fieldName))
|
||||
return getObject().referenceType().methodsByName(getterName)?.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-20
@@ -37,6 +37,7 @@ import com.sun.jdi.Method
|
||||
import org.jetbrains.kotlin.codegen.PropertyCodegen
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException
|
||||
import org.jetbrains.kotlin.idea.debugger.KotlinDebuggerSettings
|
||||
|
||||
public class KotlinClassWithDelegatedPropertyRenderer : ClassRenderer() {
|
||||
|
||||
@@ -69,24 +70,22 @@ public class KotlinClassWithDelegatedPropertyRenderer : ClassRenderer() {
|
||||
}
|
||||
|
||||
val fieldDescriptor = nodeDescriptorFactory.getFieldDescriptor(builder.getParentDescriptor(), value, field)
|
||||
children.add(nodeManager.createNode(fieldDescriptor, context))
|
||||
|
||||
if (field.name().endsWith(JvmAbi.DELEGATED_PROPERTY_NAME_SUFFIX)) {
|
||||
val method = findGetterForDelegatedProperty(value, field)
|
||||
val threadReference = context.getSuspendContext().getThread()?.getThreadReference()
|
||||
if (method != null && threadReference != null) {
|
||||
val propValue = try {
|
||||
context.getDebugProcess().invokeInstanceMethod(context, value, method, listOf<Nothing>(), context.getSuspendContext().getSuspendPolicy())
|
||||
}
|
||||
catch(e: EvaluateException) {
|
||||
e.getExceptionFromTargetVM()
|
||||
}
|
||||
if (propValue != null) {
|
||||
val delegatedPropertyDescriptor = DelegatedPropertyFieldDescriptor(
|
||||
context.getDebugProcess().getProject()!!, propValue, value, field)
|
||||
children.add(nodeManager.createNode(delegatedPropertyDescriptor, context))
|
||||
}
|
||||
val shouldRenderDelegatedProperty = KotlinDebuggerSettings.getInstance().DEBUG_RENDER_DELEGATED_PROPERTIES
|
||||
if (shouldRenderDelegatedProperty) {
|
||||
children.add(nodeManager.createNode(fieldDescriptor, context))
|
||||
}
|
||||
|
||||
val delegatedPropertyDescriptor = DelegatedPropertyFieldDescriptor(
|
||||
context.getDebugProcess().getProject()!!,
|
||||
value,
|
||||
field,
|
||||
shouldRenderDelegatedProperty)
|
||||
children.add(nodeManager.createNode(delegatedPropertyDescriptor, context))
|
||||
}
|
||||
else {
|
||||
children.add(nodeManager.createNode(fieldDescriptor, context))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,9 +96,4 @@ public class KotlinClassWithDelegatedPropertyRenderer : ClassRenderer() {
|
||||
builder.setChildren(children)
|
||||
}
|
||||
|
||||
private fun findGetterForDelegatedProperty(objRef: ObjectReference, delegate: Field): Method? {
|
||||
val fieldName = delegate.name().trimTrailing(JvmAbi.DELEGATED_PROPERTY_NAME_SUFFIX)
|
||||
val getterName = PropertyCodegen.getterName(Name.identifier(fieldName))
|
||||
return objRef.referenceType().methodsByName(getterName)?.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
LineBreakpoint created at delegatedPropertyInClassWoRenderer.kt:8
|
||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! delegatedPropertyInClassWoRenderer.DelegatedPropertyInClassWoRendererPackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
delegatedPropertyInClassWoRenderer.kt:8
|
||||
package delegatedPropertyInClassWoRenderer
|
||||
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val a = A()
|
||||
//Breakpoint!
|
||||
args.size()
|
||||
}
|
||||
|
||||
class A {
|
||||
val prop by MyDelegate()
|
||||
}
|
||||
|
||||
class MyDelegate {
|
||||
fun get(t: Any?, p: PropertyMetadata): Int = 1
|
||||
}
|
||||
|
||||
// RENDER_DELEGATED_PROPERTIES: false
|
||||
// PRINT_FRAME
|
||||
frame = main():8, DelegatedPropertyInClassWoRendererPackage$@packagePartHASH {delegatedPropertyInClassWoRenderer}
|
||||
static = static = delegatedPropertyInClassWoRenderer.DelegatedPropertyInClassWoRendererPackage$@packagePartHASH
|
||||
local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = delegatedPropertyInClassWoRenderer.kt, 5)
|
||||
local = a: delegatedPropertyInClassWoRenderer.A = {delegatedPropertyInClassWoRenderer.A@uniqueID} (sp = delegatedPropertyInClassWoRenderer.kt, 6)
|
||||
field = prop: delegatedPropertyInClassWoRenderer.MyDelegate = {delegatedPropertyInClassWoRenderer.MyDelegate@uniqueID} (sp = delegatedPropertyInClassWoRenderer.kt, 12)
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package delegatedPropertyInClassWoRenderer
|
||||
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val a = A()
|
||||
//Breakpoint!
|
||||
args.size()
|
||||
}
|
||||
|
||||
class A {
|
||||
val prop by MyDelegate()
|
||||
}
|
||||
|
||||
class MyDelegate {
|
||||
fun get(t: Any?, p: PropertyMetadata): Int = 1
|
||||
}
|
||||
|
||||
// RENDER_DELEGATED_PROPERTIES: false
|
||||
// PRINT_FRAME
|
||||
@@ -38,6 +38,7 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
private var oldSettings: DebuggerSettings by Delegates.notNull()
|
||||
private var oldIsFilterForStdlibAlreadyAdded: Boolean by Delegates.notNull()
|
||||
private var oldDisableKotlinInternalClasses: Boolean by Delegates.notNull()
|
||||
private var oldRenderDelegatedProperties: Boolean by Delegates.notNull()
|
||||
|
||||
override fun initApplication() {
|
||||
super.initApplication()
|
||||
@@ -53,6 +54,7 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
val kotlinSettings = KotlinDebuggerSettings.getInstance()
|
||||
kotlinSettings.DEBUG_IS_FILTER_FOR_STDLIB_ALREADY_ADDED = false
|
||||
kotlinSettings.DEBUG_DISABLE_KOTLIN_INTERNAL_CLASSES = fileText.getValueForSetting("DISABLE_KOTLIN_INTERNAL_CLASSES", oldDisableKotlinInternalClasses)
|
||||
kotlinSettings.DEBUG_RENDER_DELEGATED_PROPERTIES = fileText.getValueForSetting("RENDER_DELEGATED_PROPERTIES", oldRenderDelegatedProperties)
|
||||
|
||||
val debuggerSettings = DebuggerSettings.getInstance()!!
|
||||
debuggerSettings.SKIP_SYNTHETIC_METHODS = fileText.getValueForSetting("SKIP_SYNTHETIC_METHODS", oldSettings.SKIP_SYNTHETIC_METHODS)
|
||||
@@ -68,12 +70,14 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
private fun saveDefaultSettings() {
|
||||
oldIsFilterForStdlibAlreadyAdded = KotlinDebuggerSettings.getInstance().DEBUG_IS_FILTER_FOR_STDLIB_ALREADY_ADDED
|
||||
oldDisableKotlinInternalClasses = KotlinDebuggerSettings.getInstance().DEBUG_DISABLE_KOTLIN_INTERNAL_CLASSES
|
||||
oldRenderDelegatedProperties = KotlinDebuggerSettings.getInstance().DEBUG_RENDER_DELEGATED_PROPERTIES
|
||||
oldSettings = DebuggerSettings.getInstance()!!.clone()
|
||||
}
|
||||
|
||||
private fun restoreDefaultSettings() {
|
||||
KotlinDebuggerSettings.getInstance().DEBUG_IS_FILTER_FOR_STDLIB_ALREADY_ADDED = oldIsFilterForStdlibAlreadyAdded
|
||||
KotlinDebuggerSettings.getInstance().DEBUG_DISABLE_KOTLIN_INTERNAL_CLASSES = oldDisableKotlinInternalClasses
|
||||
KotlinDebuggerSettings.getInstance().DEBUG_RENDER_DELEGATED_PROPERTIES = oldRenderDelegatedProperties
|
||||
|
||||
val debuggerSettings = DebuggerSettings.getInstance()!!
|
||||
debuggerSettings.SKIP_SYNTHETIC_METHODS = oldSettings.SKIP_SYNTHETIC_METHODS
|
||||
|
||||
+6
@@ -399,6 +399,12 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
||||
doSingleBreakpointTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedPropertyInClassWoRenderer.kt")
|
||||
public void testDelegatedPropertyInClassWoRenderer() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/delegatedPropertyInClassWoRenderer.kt");
|
||||
doSingleBreakpointTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("frameAnonymousObject.kt")
|
||||
public void testFrameAnonymousObject() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/frameAnonymousObject.kt");
|
||||
|
||||
Reference in New Issue
Block a user