Field WatchPoints: add ability to stop at initializer

This commit is contained in:
Natalia Ukhorskaya
2015-06-05 18:28:43 +03:00
parent d7375e0dc4
commit 14f90f15e5
9 changed files with 199 additions and 60 deletions
@@ -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
@@ -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<JavaFieldBreakpointProperties>) : FieldBreakpoint(project, breakpoint) {
class KotlinFieldBreakpoint(
project: Project,
breakpoint: XBreakpoint<KotlinPropertyBreakpointProperties>
): BreakpointWithHighlighter<KotlinPropertyBreakpointProperties>(project, breakpoint) {
companion object {
private val LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinFieldBreakpoint")
private val CATEGORY: Key<FieldBreakpoint> = BreakpointCategory.lookup<FieldBreakpoint>("field_breakpoints")
}
private enum class BreakpointType {
@@ -127,6 +136,22 @@ class KotlinFieldBreakpoint(project: Project, breakpoint: XBreakpoint<JavaFieldB
val vm = debugProcess.getVirtualMachineProxy()
try {
if (getProperties().WATCH_INITIALIZATION) {
val sourcePosition = getSourcePosition()
if (sourcePosition != null) {
debugProcess.getPositionManager()
.locationsOfLine(refType, sourcePosition)
.filter { it.method().isConstructor() || it.method().isStaticInitializer() }
.forEach {
val request = debugProcess.getRequestsManager().createBreakpointRequest(this, it)
debugProcess.getRequestsManager().enableRequest(request)
if (LOG.isDebugEnabled()) {
LOG.debug("Breakpoint request added")
}
}
}
}
when (breakpointType) {
BreakpointType.FIELD -> {
val field = refType.fieldByName(getFieldName())
@@ -298,4 +323,67 @@ class KotlinFieldBreakpoint(project: Project, breakpoint: XBreakpoint<JavaFieldB
fun setWatchModification(value: Boolean) {
getProperties().WATCH_MODIFICATION = value
}
@TestOnly
fun setWatchInitialization(value: Boolean) {
getProperties().WATCH_INITIALIZATION = value
}
override fun getDisabledIcon(isMuted: Boolean): Icon {
val master = DebuggerManagerEx.getInstanceEx(myProject).getBreakpointManager().findMasterBreakpoint(this)
return when {
isMuted && master == null -> 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()
}
}
@@ -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<XLineBreakpoint<KotlinPropertyBreakpointProperties>>() {
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<KotlinPropertyBreakpointProperties>) {
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()
}
@@ -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<JavaFieldBreakpointProperties>, XLineBreakpointType<JavaFieldBreakpointProperties>(
public class KotlinFieldBreakpointType : JavaBreakpointType<KotlinPropertyBreakpointProperties>, XLineBreakpointType<KotlinPropertyBreakpointProperties>(
"kotlin-field", JetBundle.message("debugger.field.watchpoints.tab.title")
) {
private val delegate = JavaFieldBreakpointType()
override fun createJavaBreakpoint(project: Project, breakpoint: XBreakpoint<JavaFieldBreakpointProperties>): Breakpoint<*> {
override fun createJavaBreakpoint(project: Project, breakpoint: XBreakpoint<KotlinPropertyBreakpointProperties>): Breakpoint<*> {
return KotlinFieldBreakpoint(project, breakpoint)
}
@@ -120,12 +101,12 @@ public class KotlinFieldBreakpointType : JavaBreakpointType<JavaFieldBreakpointP
override fun getPriority() = 120
override fun createBreakpointProperties(file: VirtualFile, line: Int): JavaFieldBreakpointProperties? {
override fun createBreakpointProperties(file: VirtualFile, line: Int): KotlinPropertyBreakpointProperties? {
return KotlinPropertyBreakpointProperties()
}
override fun addBreakpoint(project: Project, parentComponent: JComponent?): XLineBreakpoint<JavaFieldBreakpointProperties>? {
var result: XLineBreakpoint<JavaFieldBreakpointProperties>? = null
override fun addBreakpoint(project: Project, parentComponent: JComponent?): XLineBreakpoint<KotlinPropertyBreakpointProperties>? {
var result: XLineBreakpoint<KotlinPropertyBreakpointProperties>? = null
val dialog = object : AddFieldBreakpointDialog(project) {
override fun validateData(): Boolean {
@@ -175,7 +156,7 @@ public class KotlinFieldBreakpointType : JavaBreakpointType<JavaFieldBreakpointP
file: JetFile,
className: String,
fieldName: String
): XLineBreakpoint<JavaFieldBreakpointProperties>? {
): XLineBreakpoint<KotlinPropertyBreakpointProperties>? {
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<JavaFieldBreakpointP
return delegate.canBeHitInOtherPlaces()
}
override fun getShortText(breakpoint: XLineBreakpoint<JavaFieldBreakpointProperties>?): String? {
return delegate.getShortText(breakpoint)
override fun getShortText(breakpoint: XLineBreakpoint<KotlinPropertyBreakpointProperties>): 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<XLineBreakpoint<JavaFieldBreakpointProperties>>? {
return KotlinFieldBreakpointPropertiesPanel() as XBreakpointCustomPropertiesPanel<XLineBreakpoint<JavaFieldBreakpointProperties>>
override fun createCustomPropertiesPanel(): XBreakpointCustomPropertiesPanel<XLineBreakpoint<KotlinPropertyBreakpointProperties>>? {
return KotlinFieldBreakpointPropertiesPanel()
}
override fun getDisplayText(breakpoint: XLineBreakpoint<JavaFieldBreakpointProperties>?): String? {
return delegate.getDisplayText(breakpoint)
override fun getDisplayText(breakpoint: XLineBreakpoint<KotlinPropertyBreakpointProperties>): String? {
val kotlinBreakpoint = BreakpointManager.getJavaBreakpoint(breakpoint) as? BreakpointWithHighlighter
if (kotlinBreakpoint != null) {
return kotlinBreakpoint.getDescription();
}
else {
return super<XLineBreakpointType>.getDisplayText(breakpoint);
}
}
override fun getEditorsProvider(): XDebuggerEditorsProvider? {
return delegate.getEditorsProvider()
}
override fun createCustomRightPropertiesPanel(project: Project): XBreakpointCustomPropertiesPanel<XLineBreakpoint<JavaFieldBreakpointProperties>>? {
return delegate.createCustomRightPropertiesPanel(project)
override fun createCustomRightPropertiesPanel(project: Project): XBreakpointCustomPropertiesPanel<XLineBreakpoint<KotlinPropertyBreakpointProperties>>? {
//TODO unsafe cast
return delegate.createCustomRightPropertiesPanel(project) as XBreakpointCustomPropertiesPanel<XLineBreakpoint<KotlinPropertyBreakpointProperties>>
}
override fun isSuspendThreadSupported(): Boolean {
@@ -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)
@Attribute var myFieldName: String = "",
@Attribute var myClassName: String = ""
): JavaBreakpointProperties<KotlinPropertyBreakpointProperties>() {
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
}
}
+11
View File
@@ -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
@@ -0,0 +1,27 @@
package fwInitializer
fun main(args: Array<String>) {
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
@@ -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)
}
@@ -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");