diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties index b8a12b86442..b1602555a38 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties @@ -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) diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerSettings.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerSettings.kt index 5e56a1e35a1..5cc82a385fe 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerSettings.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerSettings.kt @@ -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("kotlin_debugger"), Getter { + 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( return when (category) { DebuggerSettingsCategory.STEPPING -> listOf(SimpleConfigurable.create( - "reference.idesettings.debugger.kotlin", + "reference.idesettings.debugger.kotlin.stepping", "Kotlin", javaClass(), this)) + DebuggerSettingsCategory.DATA_VIEWS -> + listOf(SimpleConfigurable.create( + "reference.idesettings.debugger.kotlin.data.view", + "Kotlin", + javaClass(), + this)) else -> listOf() } } diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinDelegatedPropertyRendererConfigurableUi.form b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinDelegatedPropertyRendererConfigurableUi.form new file mode 100644 index 00000000000..58571d255d0 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinDelegatedPropertyRendererConfigurableUi.form @@ -0,0 +1,27 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinDelegatedPropertyRendererConfigurableUi.java b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinDelegatedPropertyRendererConfigurableUi.java new file mode 100644 index 00000000000..b0b631c203b --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/KotlinDelegatedPropertyRendererConfigurableUi.java @@ -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 { + 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; + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/render/DelegatedPropertyFieldDescriptor.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/render/DelegatedPropertyFieldDescriptor.kt index e64cf14e311..09d1a216136 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/render/DelegatedPropertyFieldDescriptor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/render/DelegatedPropertyFieldDescriptor.kt @@ -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(), + 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() + } } diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/render/KotlinClassWithDelegatedPropertyRenderer.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/render/KotlinClassWithDelegatedPropertyRenderer.kt index d1217dcf1d0..630ea02ad57 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/render/KotlinClassWithDelegatedPropertyRenderer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/render/KotlinClassWithDelegatedPropertyRenderer.kt @@ -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(), 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() - } } diff --git a/idea/testData/debugger/tinyApp/outs/delegatedPropertyInClassWoRenderer.out b/idea/testData/debugger/tinyApp/outs/delegatedPropertyInClassWoRenderer.out new file mode 100644 index 00000000000..ae5a6a38bfa --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/delegatedPropertyInClassWoRenderer.out @@ -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) { + 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 diff --git a/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/delegatedPropertyInClassWoRenderer.kt b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/delegatedPropertyInClassWoRenderer.kt new file mode 100644 index 00000000000..87af258f0af --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/frame/delegatedPropertyInClassWoRenderer.kt @@ -0,0 +1,20 @@ +package delegatedPropertyInClassWoRenderer + +import kotlin.properties.Delegates + +fun main(args: Array) { + 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 \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt index a1989643733..992fb2dc6d5 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt @@ -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 diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java index a5a868bd813..30ad3a3b8f8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/evaluate/KotlinEvaluateExpressionTestGenerated.java @@ -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");