Debugger: support field watchpoints for kotlin
This commit is contained in:
@@ -338,3 +338,4 @@ kotlin.compiler.js.option.output.copy.dir=O&utput directory for library &runtime
|
|||||||
# Debugger
|
# Debugger
|
||||||
debugger.filter.ignore.internal.classes=Do not step into specific Kotlin classes
|
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.data.view.delegated.properties=Calculate values of delegated properties (may affect program execution)
|
||||||
|
debugger.field.watchpoints.tab.title=Kotlin Field Watchpoints
|
||||||
|
|||||||
@@ -406,7 +406,9 @@
|
|||||||
<debugger.frameExtraVarsProvider implementation="org.jetbrains.kotlin.idea.debugger.KotlinFrameExtraVariablesProvider"/>
|
<debugger.frameExtraVarsProvider implementation="org.jetbrains.kotlin.idea.debugger.KotlinFrameExtraVariablesProvider"/>
|
||||||
<debugger.extraSteppingFilter implementation="org.jetbrains.kotlin.idea.ExtraSteppingFilter"/>
|
<debugger.extraSteppingFilter implementation="org.jetbrains.kotlin.idea.ExtraSteppingFilter"/>
|
||||||
<xdebugger.settings implementation="org.jetbrains.kotlin.idea.debugger.KotlinDebuggerSettings"/>
|
<xdebugger.settings implementation="org.jetbrains.kotlin.idea.debugger.KotlinDebuggerSettings"/>
|
||||||
|
<xdebugger.breakpointType implementation="org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinFieldBreakpointType"/>
|
||||||
<debugger.syntheticProvider implementation="org.jetbrains.kotlin.idea.debugger.filter.KotlinSyntheticTypeComponentProvider"/>
|
<debugger.syntheticProvider implementation="org.jetbrains.kotlin.idea.debugger.filter.KotlinSyntheticTypeComponentProvider"/>
|
||||||
|
<debugger.javaBreakpointHandlerFactory implementation="org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinBreakpointHandlerFactory"/>
|
||||||
|
|
||||||
<codeInsight.implementMethod language="jet" implementationClass="org.jetbrains.kotlin.idea.core.codeInsight.ImplementMethodsHandler"/>
|
<codeInsight.implementMethod language="jet" implementationClass="org.jetbrains.kotlin.idea.core.codeInsight.ImplementMethodsHandler"/>
|
||||||
<codeInsight.overrideMethod language="jet" implementationClass="org.jetbrains.kotlin.idea.codeInsight.OverrideMethodsHandler"/>
|
<codeInsight.overrideMethod language="jet" implementationClass="org.jetbrains.kotlin.idea.codeInsight.OverrideMethodsHandler"/>
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.breakpoints
|
||||||
|
|
||||||
|
import com.intellij.debugger.engine.DebugProcessImpl
|
||||||
|
import com.intellij.debugger.engine.JavaBreakpointHandler
|
||||||
|
import com.intellij.debugger.engine.JavaBreakpointHandlerFactory
|
||||||
|
import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType
|
||||||
|
|
||||||
|
public class KotlinBreakpointHandlerFactory: JavaBreakpointHandlerFactory {
|
||||||
|
override fun createHandler(process: DebugProcessImpl): JavaBreakpointHandler? {
|
||||||
|
return KotlinFieldBreakpointHandler(process)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class KotlinFieldBreakpointHandler(process: DebugProcessImpl) : JavaBreakpointHandler(javaClass<KotlinFieldBreakpointType>(), process)
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* 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.breakpoints
|
||||||
|
|
||||||
|
import com.intellij.debugger.SourcePosition
|
||||||
|
import com.intellij.debugger.impl.PositionUtil
|
||||||
|
import com.intellij.debugger.ui.breakpoints.*
|
||||||
|
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
import com.intellij.psi.*
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
|
import com.intellij.xdebugger.XDebuggerUtil
|
||||||
|
import com.intellij.xdebugger.breakpoints.XBreakpoint
|
||||||
|
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 org.jetbrains.annotations.TestOnly
|
||||||
|
import org.jetbrains.java.debugger.breakpoints.properties.JavaFieldBreakpointProperties
|
||||||
|
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.load.kotlin.PackageClassUtils
|
||||||
|
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>(
|
||||||
|
"kotlin-field", JetBundle.message("debugger.field.watchpoints.tab.title")
|
||||||
|
) {
|
||||||
|
private val delegate = JavaFieldBreakpointType()
|
||||||
|
|
||||||
|
override fun createJavaBreakpoint(project: Project, breakpoint: XBreakpoint<JavaFieldBreakpointProperties>): Breakpoint<*> {
|
||||||
|
return KotlinFieldBreakpoint(project, breakpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canPutAt(file: VirtualFile, line: Int, project: Project): Boolean {
|
||||||
|
val psiFile = PsiManager.getInstance(project).findFile(file)
|
||||||
|
|
||||||
|
if (psiFile == null || psiFile.getVirtualFile().getFileType() != JetFileType.INSTANCE) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val document = FileDocumentManager.getInstance().getDocument(file) ?: return false
|
||||||
|
|
||||||
|
var canPutAt = false
|
||||||
|
XDebuggerUtil.getInstance().iterateLine(project, document, line, fun (el: PsiElement): Boolean {
|
||||||
|
// avoid comments
|
||||||
|
if (el is PsiWhiteSpace || PsiTreeUtil.getParentOfType(el, javaClass<PsiComment>(), false) != null) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var element = el
|
||||||
|
var parent = element.getParent()
|
||||||
|
while (parent != null) {
|
||||||
|
val offset = parent.getTextOffset()
|
||||||
|
if (offset >= 0 && document.getLineNumber(offset) != line) break
|
||||||
|
|
||||||
|
element = parent
|
||||||
|
parent = element.getParent()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element is JetProperty || element is JetParameter) {
|
||||||
|
val bindingContext = (element as JetElement).analyzeAndGetResult().bindingContext
|
||||||
|
var descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element)
|
||||||
|
if (descriptor is ValueParameterDescriptor) {
|
||||||
|
descriptor = bindingContext.get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, descriptor)
|
||||||
|
}
|
||||||
|
if (descriptor is PropertyDescriptor && bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor)) {
|
||||||
|
canPutAt = true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
return canPutAt
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPriority() = 120
|
||||||
|
|
||||||
|
override fun createBreakpointProperties(file: VirtualFile, line: Int): JavaFieldBreakpointProperties? {
|
||||||
|
return delegate.createBreakpointProperties(file, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addBreakpoint(project: Project?, parentComponent: JComponent?): XLineBreakpoint<JavaFieldBreakpointProperties>? {
|
||||||
|
return delegate.addBreakpoint(project, parentComponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isAddBreakpointButtonVisible(): Boolean {
|
||||||
|
return delegate.isAddBreakpointButtonVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMutedEnabledIcon(): Icon {
|
||||||
|
return delegate.getMutedEnabledIcon()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDisabledIcon(): Icon {
|
||||||
|
return delegate.getDisabledIcon()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEnabledIcon(): Icon {
|
||||||
|
return delegate.getEnabledIcon()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMutedDisabledIcon(): Icon {
|
||||||
|
return delegate.getMutedDisabledIcon()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun canBeHitInOtherPlaces(): Boolean {
|
||||||
|
return delegate.canBeHitInOtherPlaces()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getShortText(breakpoint: XLineBreakpoint<JavaFieldBreakpointProperties>?): String? {
|
||||||
|
return delegate.getShortText(breakpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createProperties(): JavaFieldBreakpointProperties? {
|
||||||
|
return delegate.createProperties()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createCustomPropertiesPanel(): XBreakpointCustomPropertiesPanel<XLineBreakpoint<JavaFieldBreakpointProperties>>? {
|
||||||
|
return delegate.createCustomPropertiesPanel()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDisplayText(breakpoint: XLineBreakpoint<JavaFieldBreakpointProperties>?): String? {
|
||||||
|
return delegate.getDisplayText(breakpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEditorsProvider(): XDebuggerEditorsProvider? {
|
||||||
|
return delegate.getEditorsProvider()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createCustomRightPropertiesPanel(project: Project): XBreakpointCustomPropertiesPanel<XLineBreakpoint<JavaFieldBreakpointProperties>>? {
|
||||||
|
return delegate.createCustomRightPropertiesPanel(project)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSuspendThreadSupported(): Boolean {
|
||||||
|
return delegate.isSuspendThreadSupported()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class KotlinFieldBreakpoint(project: Project, breakpoint: XBreakpoint<JavaFieldBreakpointProperties>): FieldBreakpoint(project, breakpoint) {
|
||||||
|
override fun isValid(): Boolean {
|
||||||
|
if (!BreakpointWithHighlighter.isPositionValid(getXBreakpoint().getSourcePosition())) return false
|
||||||
|
|
||||||
|
return runReadAction {
|
||||||
|
val field = getField()
|
||||||
|
field != null && field.isValid()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun getField(): JetCallableDeclaration? {
|
||||||
|
val sourcePosition = getSourcePosition()
|
||||||
|
return getProperty(sourcePosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getProperty(sourcePosition: SourcePosition?): JetCallableDeclaration? {
|
||||||
|
val property: JetProperty? = PositionUtil.getPsiElementAt(getProject(), javaClass(), sourcePosition)
|
||||||
|
if (property != null) {
|
||||||
|
return property
|
||||||
|
}
|
||||||
|
val parameter: JetParameter? = PositionUtil.getPsiElementAt(getProject(), javaClass(), sourcePosition)
|
||||||
|
if (parameter != null) {
|
||||||
|
return parameter
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reload(psiFile: PsiFile?) {
|
||||||
|
val property = getProperty(getSourcePosition())
|
||||||
|
if (property != null) {
|
||||||
|
setFieldName(property.getName())
|
||||||
|
|
||||||
|
if (property is JetProperty && property.isTopLevel()) {
|
||||||
|
getProperties().myClassName = PackageClassUtils.getPackageClassFqName(property.getContainingJetFile().getPackageFqName()).asString()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val jetClass: JetClassOrObject? = PsiTreeUtil.getParentOfType(property, javaClass())
|
||||||
|
if (jetClass is JetClassOrObject) {
|
||||||
|
val fqName = jetClass.getFqName()
|
||||||
|
if (fqName != null) {
|
||||||
|
getProperties().myClassName = fqName.asString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setInstanceFiltersEnabled(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setFieldName(fieldName: String) {
|
||||||
|
getProperties().myFieldName = fieldName
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestOnly
|
||||||
|
fun setWatchAccess(value: Boolean) {
|
||||||
|
getProperties().WATCH_ACCESS = value
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestOnly
|
||||||
|
fun setWatchModification(value: Boolean) {
|
||||||
|
getProperties().WATCH_MODIFICATION = value
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
KotlinFieldBreakpoint created at fieldWatchpoints.kt:4
|
||||||
|
KotlinFieldBreakpoint created at fieldWatchpoints.kt:7
|
||||||
|
KotlinFieldBreakpoint created at fieldWatchpoints.kt:17
|
||||||
|
KotlinFieldBreakpoint created at fieldWatchpoints.kt:20
|
||||||
|
KotlinFieldBreakpoint created at fieldWatchpoints.kt:30
|
||||||
|
KotlinFieldBreakpoint created at fieldWatchpoints.kt:32
|
||||||
|
KotlinFieldBreakpoint created at fieldWatchpoints.kt:44
|
||||||
|
KotlinFieldBreakpoint created at fieldWatchpoints.kt:47
|
||||||
|
!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! fieldWatchpoints.FieldWatchpointsPackage
|
||||||
|
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
fieldWatchpoints.kt:18
|
||||||
|
fieldWatchpoints.kt:21
|
||||||
|
fieldWatchpoints.kt:59
|
||||||
|
fieldWatchpoints.kt:5
|
||||||
|
fieldWatchpoints.kt:8
|
||||||
|
fieldWatchpoints.kt:59
|
||||||
|
fieldWatchpoints.kt:11
|
||||||
|
fieldWatchpoints.kt:12
|
||||||
|
fieldWatchpoints.kt:13
|
||||||
|
fieldWatchpoints.kt:60
|
||||||
|
fieldWatchpoints.kt:24
|
||||||
|
fieldWatchpoints.kt:25
|
||||||
|
fieldWatchpoints.kt:26
|
||||||
|
fieldWatchpoints.kt:61
|
||||||
|
fieldWatchpoints.kt:0
|
||||||
|
fieldWatchpoints.kt:0
|
||||||
|
fieldWatchpoints.kt:61
|
||||||
|
fieldWatchpoints.kt:36
|
||||||
|
fieldWatchpoints.kt:37
|
||||||
|
fieldWatchpoints.kt:38
|
||||||
|
fieldWatchpoints.kt:62
|
||||||
|
fieldWatchpoints.kt:45
|
||||||
|
fieldWatchpoints.kt:48
|
||||||
|
fieldWatchpoints.kt:62
|
||||||
|
fieldWatchpoints.kt:44
|
||||||
|
fieldWatchpoints.kt:51
|
||||||
|
fieldWatchpoints.kt:47
|
||||||
|
fieldWatchpoints.kt:52
|
||||||
|
fieldWatchpoints.kt:47
|
||||||
|
fieldWatchpoints.kt:54
|
||||||
|
fieldWatchpoints.kt:63
|
||||||
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
!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! inapplicableFieldWatchpoints.InapplicableFieldWatchpointsPackage
|
||||||
|
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package fieldWatchpoints
|
||||||
|
|
||||||
|
class A {
|
||||||
|
//FieldWatchpoint! (propVal)
|
||||||
|
val propVal = 1
|
||||||
|
|
||||||
|
//FieldWatchpoint! (propVar)
|
||||||
|
var propVar = 1
|
||||||
|
|
||||||
|
fun testPublicPropertyInClass() {
|
||||||
|
propVal
|
||||||
|
propVar
|
||||||
|
propVar = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//FieldWatchpoint! (topPropVal)
|
||||||
|
val topPropVal = 1
|
||||||
|
|
||||||
|
//FieldWatchpoint! (topPropVar)
|
||||||
|
var topPropVar = 1
|
||||||
|
|
||||||
|
fun testPublicTopLevelProperty() {
|
||||||
|
topPropVal
|
||||||
|
topPropVar
|
||||||
|
topPropVar = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
class B(
|
||||||
|
//FieldWatchpoint! (bPropVal)
|
||||||
|
val bPropVal: Int,
|
||||||
|
//FieldWatchpoint! (bPropVar)
|
||||||
|
var bPropVar: Int
|
||||||
|
) {
|
||||||
|
fun testConstructorProperty() {
|
||||||
|
bPropVal
|
||||||
|
bPropVar
|
||||||
|
bPropVar = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AWithCompanion {
|
||||||
|
companion object {
|
||||||
|
//FieldWatchpoint! (compPropVal)
|
||||||
|
val compPropVal = 1
|
||||||
|
|
||||||
|
//FieldWatchpoint! (compPropVar)
|
||||||
|
var compPropVar = 1
|
||||||
|
|
||||||
|
fun testCompanionProperty() {
|
||||||
|
compPropVal
|
||||||
|
compPropVar
|
||||||
|
compPropVar = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
A().testPublicPropertyInClass()
|
||||||
|
testPublicTopLevelProperty()
|
||||||
|
B(1, 1).testConstructorProperty()
|
||||||
|
AWithCompanion.testCompanionProperty()
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP_OUT: 36
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package inapplicableFieldWatchpoints
|
||||||
|
|
||||||
|
class A {
|
||||||
|
//FieldWatchpoint! (propWithGet)
|
||||||
|
val propWithGet: Int get() = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
interface T {
|
||||||
|
//FieldWatchpoint! (propInInterface)
|
||||||
|
val propInInterface: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
//FieldWatchpoint! (localVal)
|
||||||
|
val localVal = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(
|
||||||
|
//FieldWatchpoint! (funParam)
|
||||||
|
funParam: Int
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger
|
package org.jetbrains.kotlin.idea.debugger
|
||||||
|
|
||||||
|
import com.intellij.debugger.DebuggerInvocationUtil
|
||||||
|
import com.intellij.debugger.DebuggerManagerEx
|
||||||
import com.intellij.debugger.engine.DebugProcessImpl
|
import com.intellij.debugger.engine.DebugProcessImpl
|
||||||
import com.intellij.debugger.engine.SuspendContextImpl
|
import com.intellij.debugger.engine.SuspendContextImpl
|
||||||
import com.intellij.debugger.engine.MethodFilter
|
import com.intellij.debugger.engine.MethodFilter
|
||||||
@@ -33,6 +35,28 @@ import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes
|
|||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||||
import com.intellij.debugger.impl.DebuggerContextImpl
|
import com.intellij.debugger.impl.DebuggerContextImpl
|
||||||
|
import com.intellij.debugger.ui.breakpoints.BreakpointManager
|
||||||
|
import com.intellij.debugger.ui.breakpoints.FieldBreakpoint
|
||||||
|
import com.intellij.debugger.ui.breakpoints.JavaFieldBreakpointType
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.application.ModalityState
|
||||||
|
import com.intellij.openapi.editor.Document
|
||||||
|
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||||
|
import com.intellij.openapi.util.Computable
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.psi.search.FilenameIndex
|
||||||
|
import com.intellij.xdebugger.XDebuggerManager
|
||||||
|
import com.intellij.xdebugger.XDebuggerUtil
|
||||||
|
import com.intellij.xdebugger.breakpoints.*
|
||||||
|
import org.jetbrains.java.debugger.breakpoints.properties.JavaFieldBreakpointProperties
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinFieldBreakpoint
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinFieldBreakpointType
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
|
import org.jetbrains.kotlin.psi.JetFile
|
||||||
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
|
import javax.swing.SwingUtilities
|
||||||
|
|
||||||
abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||||
private var oldSettings: DebuggerSettings by Delegates.notNull()
|
private var oldSettings: DebuggerSettings by Delegates.notNull()
|
||||||
@@ -154,4 +178,58 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
|||||||
resume(this)
|
resume(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun createBreakpoints(file: PsiFile?) {
|
||||||
|
super.createBreakpoints(file)
|
||||||
|
|
||||||
|
if (file == null) return
|
||||||
|
|
||||||
|
val document = PsiDocumentManager.getInstance(myProject).getDocument(file) ?: return
|
||||||
|
val breakpointManager = XDebuggerManager.getInstance(myProject).getBreakpointManager()
|
||||||
|
val breakpointType = javaClass<KotlinFieldBreakpointType>() as Class<out XBreakpointType<XBreakpoint<XBreakpointProperties<*>>, XBreakpointProperties<*>>>
|
||||||
|
val type = XDebuggerUtil.getInstance().findBreakpointType<XBreakpoint<XBreakpointProperties<*>>>(breakpointType) as KotlinFieldBreakpointType
|
||||||
|
val virtualFile = file.getVirtualFile()
|
||||||
|
|
||||||
|
val runnable = {
|
||||||
|
var offset = -1;
|
||||||
|
while (true) {
|
||||||
|
offset = document.getText().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 lineIndex = commentLine + 1
|
||||||
|
val fieldName = comment.substringAfter("//FieldWatchpoint! (").substringBefore(")")
|
||||||
|
|
||||||
|
if (!type.canPutAt(virtualFile, lineIndex, myProject)) continue
|
||||||
|
|
||||||
|
val xBreakpoint = runWriteAction {
|
||||||
|
breakpointManager.addLineBreakpoint(
|
||||||
|
type as XLineBreakpointType<XBreakpointProperties<*>>,
|
||||||
|
virtualFile.getUrl(),
|
||||||
|
lineIndex,
|
||||||
|
type.createBreakpointProperties(virtualFile, lineIndex)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val javaBreakpoint = BreakpointManager.getJavaBreakpoint(xBreakpoint)
|
||||||
|
if (javaBreakpoint is KotlinFieldBreakpoint) {
|
||||||
|
javaBreakpoint.setFieldName(fieldName)
|
||||||
|
javaBreakpoint.setWatchAccess(true)
|
||||||
|
javaBreakpoint.setWatchModification(true)
|
||||||
|
BreakpointManager.addBreakpoint(javaBreakpoint)
|
||||||
|
println("KotlinFieldBreakpoint created at ${file.getVirtualFile().getName()}:$lineIndex", ProcessOutputTypes.SYSTEM)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SwingUtilities.isEventDispatchThread()) {
|
||||||
|
DebuggerInvocationUtil.invokeAndWait(myProject, runnable, ModalityState.defaultModalityState())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
runnable.invoke()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -272,6 +272,27 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/debugger/tinyApp/src/stepOut")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class StepOut extends AbstractKotlinSteppingTest {
|
||||||
|
public void testAllFilesPresentInStepOut() throws Exception {
|
||||||
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepOut"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("fieldWatchpoints.kt")
|
||||||
|
public void testFieldWatchpoints() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepOut/fieldWatchpoints.kt");
|
||||||
|
doStepOutTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inapplicableFieldWatchpoints.kt")
|
||||||
|
public void testInapplicableFieldWatchpoints() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepOut/inapplicableFieldWatchpoints.kt");
|
||||||
|
doStepOutTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/debugger/tinyApp/src/filters")
|
@TestMetadata("idea/testData/debugger/tinyApp/src/filters")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user