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 38ab025ef01..4d9b3084b20 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties @@ -327,3 +327,6 @@ kotlin.compiler.js.option.output.copy.dir=O&utput directory for library &runtime 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) debugger.field.watchpoints.tab.title=Kotlin Field Watchpoints +debugger.field.watchpoints.properties.panel.field.access.label=Field &access +debugger.field.watchpoints.properties.panel.field.modification.label=Field &modification +debugger.field.watchpoints.properties.panel.field.initialization.label=Field &initialization diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpoint.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpoint.kt index 2140d1eb8a7..4203cb01d94 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpoint.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpoint.kt @@ -17,15 +17,20 @@ package org.jetbrains.kotlin.idea.debugger.breakpoints import com.intellij.debugger.DebuggerBundle +import com.intellij.debugger.DebuggerManagerEx import com.intellij.debugger.SourcePosition import com.intellij.debugger.engine.DebugProcessImpl import com.intellij.debugger.engine.evaluation.EvaluationContextImpl import com.intellij.debugger.impl.PositionUtil import com.intellij.debugger.requests.Requestor +import com.intellij.debugger.ui.breakpoints.BreakpointCategory import com.intellij.debugger.ui.breakpoints.BreakpointWithHighlighter import com.intellij.debugger.ui.breakpoints.FieldBreakpoint +import com.intellij.icons.AllIcons import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.util.PsiTreeUtil import com.intellij.xdebugger.breakpoints.XBreakpoint @@ -36,7 +41,6 @@ import com.sun.jdi.event.* import com.sun.jdi.request.EventRequest import com.sun.jdi.request.MethodEntryRequest import org.jetbrains.annotations.TestOnly -import org.jetbrains.java.debugger.breakpoints.properties.JavaFieldBreakpointProperties import org.jetbrains.kotlin.codegen.PropertyCodegen import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor @@ -46,10 +50,15 @@ import org.jetbrains.kotlin.load.kotlin.PackageClassUtils import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext +import javax.swing.Icon -class KotlinFieldBreakpoint(project: Project, breakpoint: XBreakpoint) : FieldBreakpoint(project, breakpoint) { +class KotlinFieldBreakpoint( + project: Project, + breakpoint: XBreakpoint +): BreakpointWithHighlighter(project, breakpoint) { companion object { private val LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinFieldBreakpoint") + private val CATEGORY: Key = BreakpointCategory.lookup("field_breakpoints") } private enum class BreakpointType { @@ -127,6 +136,22 @@ class KotlinFieldBreakpoint(project: Project, breakpoint: XBreakpoint { val field = refType.fieldByName(getFieldName()) @@ -298,4 +323,67 @@ class KotlinFieldBreakpoint(project: Project, breakpoint: XBreakpoint AllIcons.Debugger.Db_muted_disabled_field_breakpoint + isMuted && master != null -> AllIcons.Debugger.Db_muted_dep_field_breakpoint + master != null -> AllIcons.Debugger.Db_dep_field_breakpoint + else -> AllIcons.Debugger.Db_disabled_field_breakpoint + } + } + + override fun getSetIcon(isMuted: Boolean): Icon { + return when { + isMuted -> AllIcons.Debugger.Db_muted_field_breakpoint + else -> AllIcons.Debugger.Db_field_breakpoint + } + } + + override fun getInvalidIcon(isMuted: Boolean): Icon { + return when { + isMuted -> AllIcons.Debugger.Db_muted_invalid_field_breakpoint + else -> AllIcons.Debugger.Db_invalid_field_breakpoint + } + } + + override fun getVerifiedIcon(isMuted: Boolean): Icon { + return when { + isMuted -> AllIcons.Debugger.Db_muted_verified_field_breakpoint + else -> AllIcons.Debugger.Db_verified_field_breakpoint + } + } + + override fun getVerifiedWarningsIcon(isMuted: Boolean): Icon { + return when { + isMuted -> AllIcons.Debugger.Db_muted_field_warning_breakpoint + else -> AllIcons.Debugger.Db_field_warning_breakpoint + } + } + + override fun getCategory() = CATEGORY + + override fun getDisplayName(): String? { + if (!isValid()) { + return DebuggerBundle.message("status.breakpoint.invalid") + } + val className = getClassName() + return if (className != null && !className.isEmpty()) className + "." + getFieldName() else getFieldName() + } + + private fun getFieldName(): String { + val declaration = getField() + return runReadAction { declaration?.getName() } ?: "unknown" + } + + override fun getEvaluationElement(): PsiElement? { + return getField() + } + } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointPropertiesPanel.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointPropertiesPanel.kt index 875ead28016..ddeeda20bdf 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointPropertiesPanel.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointPropertiesPanel.kt @@ -22,6 +22,7 @@ import com.intellij.util.ui.DialogUtil import com.intellij.xdebugger.breakpoints.XLineBreakpoint import com.intellij.xdebugger.breakpoints.ui.XBreakpointCustomPropertiesPanel import com.intellij.xdebugger.impl.breakpoints.XBreakpointBase +import org.jetbrains.kotlin.idea.JetBundle import java.awt.BorderLayout import java.awt.event.ActionEvent import java.awt.event.ActionListener @@ -32,13 +33,16 @@ import javax.swing.JPanel import kotlin.properties.Delegates public class KotlinFieldBreakpointPropertiesPanel: XBreakpointCustomPropertiesPanel>() { + private var myWatchInitializationCheckBox: JCheckBox by Delegates.notNull() private var myWatchAccessCheckBox: JCheckBox by Delegates.notNull() private var myWatchModificationCheckBox: JCheckBox by Delegates.notNull() override fun getComponent(): JComponent { - myWatchAccessCheckBox = JCheckBox(DebuggerBundle.message("label.filed.breakpoint.properties.panel.field.access")) - myWatchModificationCheckBox = JCheckBox(DebuggerBundle.message("label.filed.breakpoint.properties.panel.field.modification")) + myWatchInitializationCheckBox = JCheckBox(JetBundle.message("debugger.field.watchpoints.properties.panel.field.initialization.label")) + myWatchAccessCheckBox = JCheckBox(JetBundle.message("debugger.field.watchpoints.properties.panel.field.access.label")) + myWatchModificationCheckBox = JCheckBox(JetBundle.message("debugger.field.watchpoints.properties.panel.field.modification.label")) + DialogUtil.registerMnemonic(myWatchInitializationCheckBox) DialogUtil.registerMnemonic(myWatchAccessCheckBox) DialogUtil.registerMnemonic(myWatchModificationCheckBox) @@ -49,6 +53,7 @@ public class KotlinFieldBreakpointPropertiesPanel: XBreakpointCustomPropertiesPa } val watchBox = Box.createVerticalBox() + watchBox.addNewPanelForCheckBox(myWatchInitializationCheckBox) watchBox.addNewPanelForCheckBox(myWatchAccessCheckBox) watchBox.addNewPanelForCheckBox(myWatchModificationCheckBox) @@ -59,24 +64,11 @@ public class KotlinFieldBreakpointPropertiesPanel: XBreakpointCustomPropertiesPa innerPanel.add(Box.createHorizontalStrut(3), BorderLayout.EAST) mainPanel.add(innerPanel, BorderLayout.NORTH) mainPanel.setBorder(IdeBorderFactory.createTitledBorder(DebuggerBundle.message("label.group.watch.events"), true)) - - val listener = object : ActionListener { - override fun actionPerformed(e: ActionEvent) { - if (!myWatchAccessCheckBox.isSelected() && !myWatchModificationCheckBox.isSelected()) { - when(e.getSource()) { - myWatchAccessCheckBox -> myWatchModificationCheckBox.setSelected(true) - myWatchModificationCheckBox -> myWatchAccessCheckBox.setSelected(true) - } - } - } - } - myWatchAccessCheckBox.addActionListener(listener) - myWatchModificationCheckBox.addActionListener(listener) - return mainPanel } override fun loadFrom(breakpoint: XLineBreakpoint) { + myWatchInitializationCheckBox.setSelected(breakpoint.getProperties().WATCH_INITIALIZATION) myWatchAccessCheckBox.setSelected(breakpoint.getProperties().WATCH_ACCESS) myWatchModificationCheckBox.setSelected(breakpoint.getProperties().WATCH_MODIFICATION) } @@ -88,6 +80,9 @@ public class KotlinFieldBreakpointPropertiesPanel: XBreakpointCustomPropertiesPa changed = breakpoint.getProperties().WATCH_MODIFICATION != myWatchModificationCheckBox.isSelected() || changed breakpoint.getProperties().WATCH_MODIFICATION = myWatchModificationCheckBox.isSelected() + changed = breakpoint.getProperties().WATCH_INITIALIZATION != myWatchInitializationCheckBox.isSelected() || changed + breakpoint.getProperties().WATCH_INITIALIZATION = myWatchInitializationCheckBox.isSelected() + if (changed) { (breakpoint as XBreakpointBase<*, *, *>).fireBreakpointChanged() } diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointType.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointType.kt index b25bf808be1..bf6fb3b73ad 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointType.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointType.kt @@ -17,13 +17,7 @@ package org.jetbrains.kotlin.idea.debugger.breakpoints import com.intellij.debugger.DebuggerBundle -import com.intellij.debugger.SourcePosition -import com.intellij.debugger.engine.DebugProcessImpl -import com.intellij.debugger.engine.evaluation.EvaluationContextImpl -import com.intellij.debugger.impl.PositionUtil -import com.intellij.debugger.requests.Requestor import com.intellij.debugger.ui.breakpoints.* -import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.project.Project import com.intellij.openapi.ui.Messages @@ -38,39 +32,26 @@ import com.intellij.xdebugger.breakpoints.XLineBreakpoint import com.intellij.xdebugger.breakpoints.XLineBreakpointType import com.intellij.xdebugger.breakpoints.ui.XBreakpointCustomPropertiesPanel import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider -import com.sun.jdi.AbsentInformationException -import com.sun.jdi.Method -import com.sun.jdi.ObjectCollectedException -import com.sun.jdi.ReferenceType -import com.sun.jdi.event.* -import com.sun.jdi.request.EventRequest -import com.sun.jdi.request.MethodEntryRequest -import org.jetbrains.annotations.TestOnly -import org.jetbrains.java.debugger.breakpoints.properties.JavaFieldBreakpointProperties import org.jetbrains.kotlin.asJava.KotlinLightClass import org.jetbrains.kotlin.asJava.KotlinLightClassForExplicitDeclaration import org.jetbrains.kotlin.asJava.KotlinLightClassForPackage -import org.jetbrains.kotlin.codegen.PropertyCodegen import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.idea.JetBundle import org.jetbrains.kotlin.idea.JetFileType import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult -import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runWriteAction -import org.jetbrains.kotlin.load.kotlin.PackageClassUtils -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import javax.swing.Icon import javax.swing.JComponent -public class KotlinFieldBreakpointType : JavaBreakpointType, XLineBreakpointType( +public class KotlinFieldBreakpointType : JavaBreakpointType, XLineBreakpointType( "kotlin-field", JetBundle.message("debugger.field.watchpoints.tab.title") ) { private val delegate = JavaFieldBreakpointType() - override fun createJavaBreakpoint(project: Project, breakpoint: XBreakpoint): Breakpoint<*> { + override fun createJavaBreakpoint(project: Project, breakpoint: XBreakpoint): Breakpoint<*> { return KotlinFieldBreakpoint(project, breakpoint) } @@ -120,12 +101,12 @@ public class KotlinFieldBreakpointType : JavaBreakpointType? { - var result: XLineBreakpoint? = null + override fun addBreakpoint(project: Project, parentComponent: JComponent?): XLineBreakpoint? { + var result: XLineBreakpoint? = null val dialog = object : AddFieldBreakpointDialog(project) { override fun validateData(): Boolean { @@ -175,7 +156,7 @@ public class KotlinFieldBreakpointType : JavaBreakpointType? { + ): XLineBreakpoint? { val project = file.getProject() val property = declaration.getDeclarations().firstOrNull { it is JetProperty && it.getName() == fieldName } ?: return null @@ -219,28 +200,37 @@ public class KotlinFieldBreakpointType : JavaBreakpointType?): String? { - return delegate.getShortText(breakpoint) + override fun getShortText(breakpoint: XLineBreakpoint): String? { + val properties = breakpoint.getProperties() + val className = properties.myClassName + return if (!className.isEmpty()) className + "." + properties.myFieldName else properties.myFieldName } - override fun createProperties(): JavaFieldBreakpointProperties? { + override fun createProperties(): KotlinPropertyBreakpointProperties? { return KotlinPropertyBreakpointProperties() } - override fun createCustomPropertiesPanel(): XBreakpointCustomPropertiesPanel>? { - return KotlinFieldBreakpointPropertiesPanel() as XBreakpointCustomPropertiesPanel> + override fun createCustomPropertiesPanel(): XBreakpointCustomPropertiesPanel>? { + return KotlinFieldBreakpointPropertiesPanel() } - override fun getDisplayText(breakpoint: XLineBreakpoint?): String? { - return delegate.getDisplayText(breakpoint) + override fun getDisplayText(breakpoint: XLineBreakpoint): String? { + val kotlinBreakpoint = BreakpointManager.getJavaBreakpoint(breakpoint) as? BreakpointWithHighlighter + if (kotlinBreakpoint != null) { + return kotlinBreakpoint.getDescription(); + } + else { + return super.getDisplayText(breakpoint); + } } override fun getEditorsProvider(): XDebuggerEditorsProvider? { return delegate.getEditorsProvider() } - override fun createCustomRightPropertiesPanel(project: Project): XBreakpointCustomPropertiesPanel>? { - return delegate.createCustomRightPropertiesPanel(project) + override fun createCustomRightPropertiesPanel(project: Project): XBreakpointCustomPropertiesPanel>? { + //TODO unsafe cast + return delegate.createCustomRightPropertiesPanel(project) as XBreakpointCustomPropertiesPanel> } override fun isSuspendThreadSupported(): Boolean { diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinPropertyBreakpointProperties.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinPropertyBreakpointProperties.kt index d48650e7297..3f115ad698c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinPropertyBreakpointProperties.kt +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinPropertyBreakpointProperties.kt @@ -16,11 +16,28 @@ package org.jetbrains.kotlin.idea.debugger.breakpoints -import com.intellij.util.xmlb.annotations.Attribute import org.jetbrains.java.debugger.breakpoints.properties.JavaBreakpointProperties import org.jetbrains.java.debugger.breakpoints.properties.JavaFieldBreakpointProperties +import com.intellij.util.xmlb.annotations.Attribute + public class KotlinPropertyBreakpointProperties( - propertyName: String = "", - className: String = "" -): JavaFieldBreakpointProperties(propertyName, className) \ No newline at end of file + @Attribute var myFieldName: String = "", + @Attribute var myClassName: String = "" +): JavaBreakpointProperties() { + public var WATCH_MODIFICATION: Boolean = true + public var WATCH_ACCESS: Boolean = false + public var WATCH_INITIALIZATION: Boolean = false + + override fun getState() = this + + override fun loadState(state: KotlinPropertyBreakpointProperties) { + super.loadState(state) + + WATCH_MODIFICATION = state.WATCH_MODIFICATION + WATCH_ACCESS = state.WATCH_ACCESS + WATCH_INITIALIZATION = state.WATCH_INITIALIZATION + myFieldName = state.myFieldName + myClassName = state.myClassName + } +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/outs/fwInitializer.out b/idea/testData/debugger/tinyApp/outs/fwInitializer.out new file mode 100644 index 00000000000..0ac9c3e644f --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/fwInitializer.out @@ -0,0 +1,11 @@ +KotlinFieldBreakpoint created at fwInitializer.kt:11 +KotlinFieldBreakpoint created at fwInitializer.kt:16 +KotlinFieldBreakpoint created at fwInitializer.kt:20 +!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! fwInitializer.FwInitializerPackage +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +fwInitializer.kt:12 +fwInitializer.kt:17 +fwInitializer.kt:21 +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/stepping/custom/fwInitializer.kt b/idea/testData/debugger/tinyApp/src/stepping/custom/fwInitializer.kt new file mode 100644 index 00000000000..1ff3f7d709a --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/custom/fwInitializer.kt @@ -0,0 +1,27 @@ +package fwInitializer + +fun main(args: Array) { + A() +} + +fun foo(): Int { + return 1 +} + +//FieldWatchpoint! (topLevelProp) +val topLevelProp = foo() + +class A { + companion object { + //FieldWatchpoint! (companionObjProp) + val companionObjProp = foo() + } + + //FieldWatchpoint! (prop) + val prop = foo() +} + +// RESUME: 2 +// WATCH_FIELD_INITIALISATION: true +// WATCH_FIELD_MODIFICATION: false +// WATCH_FIELD_ACCESS: false \ 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 ab4ec8306a8..5f9fc13054a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt @@ -187,12 +187,13 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() { val runnable = { var offset = -1; while (true) { - offset = document.getText().indexOf("FieldWatchpoint!", offset + 1) + val fileText = document.getText() + offset = fileText.indexOf("FieldWatchpoint!", offset + 1) if (offset == -1) break val commentLine = document.getLineNumber(offset) - val comment = document.getText().substring(document.getLineStartOffset(commentLine), document.getLineEndOffset(commentLine)) + val comment = fileText.substring(document.getLineStartOffset(commentLine), document.getLineEndOffset(commentLine)) val lineIndex = commentLine + 1 val fieldName = comment.substringAfter("//FieldWatchpoint! (").substringBefore(")") @@ -211,8 +212,9 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() { val javaBreakpoint = BreakpointManager.getJavaBreakpoint(xBreakpoint) if (javaBreakpoint is KotlinFieldBreakpoint) { javaBreakpoint.setFieldName(fieldName) - javaBreakpoint.setWatchAccess(true) - javaBreakpoint.setWatchModification(true) + javaBreakpoint.setWatchAccess(fileText.getValueForSetting("WATCH_FIELD_ACCESS", true)) + javaBreakpoint.setWatchModification(fileText.getValueForSetting("WATCH_FIELD_MODIFICATION", true)) + javaBreakpoint.setWatchInitialization(fileText.getValueForSetting("WATCH_FIELD_INITIALISATION", false)) BreakpointManager.addBreakpoint(javaBreakpoint) println("KotlinFieldBreakpoint created at ${file.getVirtualFile().getName()}:$lineIndex", ProcessOutputTypes.SYSTEM) } diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java index a8f1f78371c..e0cccc2c8a5 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java @@ -388,6 +388,12 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doCustomTest(fileName); } + @TestMetadata("fwInitializer.kt") + public void testFwInitializer() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/fwInitializer.kt"); + doCustomTest(fileName); + } + @TestMetadata("fwPropertyInInterface.kt") public void testFwPropertyInInterface() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/fwPropertyInInterface.kt");