Debugger: support stepping over inline function
This commit is contained in:
@@ -637,6 +637,7 @@ fun main(args: Array<String>) {
|
||||
model("debugger/tinyApp/src/stepping/stepIntoAndSmartStepInto", testMethod = "doSmartStepIntoTest", testClassName = "SmartStepInto")
|
||||
model("debugger/tinyApp/src/stepping/stepInto", testMethod = "doStepIntoTest", testClassName = "StepIntoOnly")
|
||||
model("debugger/tinyApp/src/stepping/stepOut", testMethod = "doStepOutTest")
|
||||
model("debugger/tinyApp/src/stepping/stepOver", testMethod = "doStepOverTest")
|
||||
model("debugger/tinyApp/src/stepping/filters", testMethod = "doStepIntoTest")
|
||||
model("debugger/tinyApp/src/stepping/custom", testMethod = "doCustomTest")
|
||||
}
|
||||
|
||||
@@ -447,8 +447,11 @@
|
||||
<debugger.extraSteppingFilter implementation="org.jetbrains.kotlin.idea.ExtraSteppingFilter"/>
|
||||
<xdebugger.settings implementation="org.jetbrains.kotlin.idea.debugger.KotlinDebuggerSettings"/>
|
||||
<xdebugger.breakpointType implementation="org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinFieldBreakpointType"/>
|
||||
<xdebugger.breakpointType implementation="org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinLineBreakpointType" order="first"/>
|
||||
<debugger.syntheticProvider implementation="org.jetbrains.kotlin.idea.debugger.filter.KotlinSyntheticTypeComponentProvider"/>
|
||||
<debugger.javaBreakpointHandlerFactory implementation="org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinBreakpointHandlerFactory"/>
|
||||
<debugger.javaBreakpointHandlerFactory implementation="org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinFieldBreakpointHandlerFactory"/>
|
||||
<debugger.javaBreakpointHandlerFactory implementation="org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinLineBreakpointHandlerFactory"/>
|
||||
<debugger.jvmSteppingCommandProvider implementation="org.jetbrains.kotlin.idea.debugger.stepping.KotlinSteppingCommandProvider"/>
|
||||
|
||||
<codeInsight.implementMethod language="jet" implementationClass="org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler"/>
|
||||
<codeInsight.overrideMethod language="jet" implementationClass="org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMembersHandler"/>
|
||||
|
||||
+9
-2
@@ -21,10 +21,17 @@ import com.intellij.debugger.engine.JavaBreakpointHandler
|
||||
import com.intellij.debugger.engine.JavaBreakpointHandlerFactory
|
||||
import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType
|
||||
|
||||
public class KotlinBreakpointHandlerFactory: JavaBreakpointHandlerFactory {
|
||||
public class KotlinFieldBreakpointHandlerFactory : JavaBreakpointHandlerFactory {
|
||||
override fun createHandler(process: DebugProcessImpl): JavaBreakpointHandler? {
|
||||
return KotlinFieldBreakpointHandler(process)
|
||||
}
|
||||
}
|
||||
|
||||
public class KotlinFieldBreakpointHandler(process: DebugProcessImpl) : JavaBreakpointHandler(javaClass<KotlinFieldBreakpointType>(), process)
|
||||
public class KotlinLineBreakpointHandlerFactory: JavaBreakpointHandlerFactory {
|
||||
override fun createHandler(process: DebugProcessImpl): JavaBreakpointHandler? {
|
||||
return KotlinLineBreakpointHandler(process)
|
||||
}
|
||||
}
|
||||
|
||||
public class KotlinFieldBreakpointHandler(process: DebugProcessImpl) : JavaBreakpointHandler(javaClass<KotlinFieldBreakpointType>(), process)
|
||||
public class KotlinLineBreakpointHandler(process: DebugProcessImpl) : JavaBreakpointHandler(javaClass<KotlinLineBreakpointType>(), process)
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.DebuggerBundle;
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType;
|
||||
import com.intellij.debugger.ui.breakpoints.LineBreakpoint;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.psi.JetFunction;
|
||||
|
||||
public class KotlinLineBreakpointType extends JavaLineBreakpointType {
|
||||
public KotlinLineBreakpointType() {
|
||||
super("kotlin-line", "Kotlin Line Breakpoints");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchesPosition(@NotNull LineBreakpoint<?> breakpoint, @NotNull SourcePosition position) {
|
||||
if (super.matchesPosition(breakpoint, position)) return true;
|
||||
|
||||
PsiElement containingMethod = getContainingMethod(breakpoint);
|
||||
if (containingMethod == null) return false;
|
||||
return inTheMethod(position, containingMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiElement getContainingMethod(@NotNull LineBreakpoint<?> breakpoint) {
|
||||
SourcePosition position = breakpoint.getSourcePosition();
|
||||
if (position == null) return null;
|
||||
|
||||
return getContainingMethod(position.getElementAt());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiElement getContainingMethod(@Nullable PsiElement elem) {
|
||||
//noinspection unchecked
|
||||
return PsiTreeUtil.getParentOfType(elem, JetFunction.class);
|
||||
}
|
||||
|
||||
public static boolean inTheMethod(@NotNull SourcePosition pos, @NotNull PsiElement method) {
|
||||
PsiElement elem = pos.getElementAt();
|
||||
if (elem == null) return false;
|
||||
return Comparing.equal(getContainingMethod(elem), method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPutAt(@NotNull VirtualFile file, int line, @NotNull Project project) {
|
||||
return BreakpointsPackage.canPutAt(file, line, project, getClass());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -16,34 +16,30 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.breakpoints
|
||||
|
||||
import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.xdebugger.XDebuggerUtil
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.idea.JetFileType
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.psi.JetElement
|
||||
import org.jetbrains.kotlin.psi.JetParameter
|
||||
import org.jetbrains.kotlin.psi.JetProperty
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
|
||||
fun canPutAt(file: VirtualFile, line: Int, project: Project, breakpointTypeClass: Class<*>): Boolean {
|
||||
val psiFile = PsiManager.getInstance(project).findFile(file)
|
||||
|
||||
if (psiFile == null || psiFile.getVirtualFile().getFileType() != JetFileType.INSTANCE) {
|
||||
if (psiFile == null || psiFile.virtualFile.fileType != JetFileType.INSTANCE) {
|
||||
return false
|
||||
}
|
||||
|
||||
val document = FileDocumentManager.getInstance().getDocument(file) ?: return false
|
||||
|
||||
var canPutAt = false
|
||||
var result: Class<*>? = null
|
||||
XDebuggerUtil.getInstance().iterateLine(project, document, line, fun (el: PsiElement): Boolean {
|
||||
// avoid comments
|
||||
if (el is PsiWhiteSpace || PsiTreeUtil.getParentOfType(el, javaClass<PsiComment>(), false) != null) {
|
||||
@@ -51,13 +47,13 @@ fun canPutAt(file: VirtualFile, line: Int, project: Project, breakpointTypeClass
|
||||
}
|
||||
|
||||
var element = el
|
||||
var parent = element.getParent()
|
||||
var parent = element.parent
|
||||
while (parent != null) {
|
||||
val offset = parent.getTextOffset()
|
||||
val offset = parent.textOffset
|
||||
if (offset >= 0 && document.getLineNumber(offset) != line) break
|
||||
|
||||
element = parent
|
||||
parent = element.getParent()
|
||||
parent = element.parent
|
||||
}
|
||||
|
||||
if (element is JetProperty || element is JetParameter) {
|
||||
@@ -67,15 +63,21 @@ fun canPutAt(file: VirtualFile, line: Int, project: Project, breakpointTypeClass
|
||||
descriptor = bindingContext.get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, descriptor)
|
||||
}
|
||||
if (descriptor is PropertyDescriptor) {
|
||||
canPutAt = true
|
||||
result = KotlinFieldBreakpointType::class.java
|
||||
}
|
||||
else {
|
||||
result = KotlinLineBreakpointType::class.java
|
||||
}
|
||||
return false
|
||||
}
|
||||
else {
|
||||
result = KotlinLineBreakpointType::class.java
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return canPutAt
|
||||
return result == breakpointTypeClass
|
||||
}
|
||||
|
||||
|
||||
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.stepping
|
||||
|
||||
import com.intellij.debugger.SourcePosition
|
||||
import com.intellij.debugger.engine.ContextUtil
|
||||
import com.intellij.debugger.engine.DebugProcessImpl
|
||||
import com.intellij.debugger.engine.SuspendContextImpl
|
||||
import com.intellij.debugger.engine.events.DebuggerCommandImpl
|
||||
import com.intellij.debugger.impl.JvmSteppingCommandProvider
|
||||
import com.intellij.util.concurrency.Semaphore
|
||||
import com.intellij.xdebugger.impl.XSourcePositionImpl
|
||||
import com.sun.jdi.ReferenceType
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.idea.core.refactoring.getLineCount
|
||||
import org.jetbrains.kotlin.idea.core.refactoring.getLineStartOffset
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
|
||||
public class KotlinSteppingCommandProvider: JvmSteppingCommandProvider() {
|
||||
override fun getStepOverCommand(
|
||||
suspendContext: SuspendContextImpl?,
|
||||
ignoreBreakpoints: Boolean,
|
||||
stepSize: Int
|
||||
): DebugProcessImpl.ResumeCommand? {
|
||||
if (suspendContext == null) return null
|
||||
|
||||
val semaphore = Semaphore()
|
||||
semaphore.down()
|
||||
|
||||
var sourcePosition : SourcePosition? = null
|
||||
var allClasses: List<ReferenceType>? = null
|
||||
val worker = object : DebuggerCommandImpl() {
|
||||
override fun action() {
|
||||
try {
|
||||
sourcePosition = runReadAction { ContextUtil.getSourcePosition(suspendContext) }
|
||||
if (sourcePosition != null) {
|
||||
allClasses = suspendContext.debugProcess.positionManager.getAllClasses(sourcePosition!!)
|
||||
}
|
||||
}
|
||||
finally {
|
||||
semaphore.up()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspendContext.debugProcess.managerThread?.invoke(worker)
|
||||
|
||||
for (i in 0..25) {
|
||||
if (semaphore.waitFor(20)) break
|
||||
}
|
||||
|
||||
val computedSourcePosition = sourcePosition ?: return null
|
||||
val computedReferenceType = allClasses?.firstOrNull() ?: return null
|
||||
|
||||
val file = computedSourcePosition.file as? JetFile ?: return null
|
||||
|
||||
val inlinedArguments = getInlinedArgumentsIfAny(computedSourcePosition) ?: return null
|
||||
|
||||
val locations = computedReferenceType.allLineLocations()
|
||||
val countOfLinesInFile = file.getLineCount()
|
||||
|
||||
val nextLine = computedSourcePosition.line + 2 /* +1 - because of locations are counted from 1 and +1 - because we want next line */
|
||||
|
||||
for (lineNumber in nextLine..countOfLinesInFile) {
|
||||
val location = locations.firstOrNull { it.lineNumber() == lineNumber }
|
||||
if (location != null) {
|
||||
val lineStartOffset = file.getLineStartOffset(lineNumber - 1) ?: continue
|
||||
if (inlinedArguments.any { it.textRange.contains(lineStartOffset) }) continue
|
||||
|
||||
val elementAt = file.findElementAt(lineStartOffset)
|
||||
val xPosition = XSourcePositionImpl.createByElement(elementAt) ?: return null
|
||||
|
||||
return suspendContext.debugProcess.createRunToCursorCommand(suspendContext, xPosition, ignoreBreakpoints)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getInlinedArgumentsIfAny(sourcePosition: SourcePosition): List<JetFunction>? {
|
||||
val file = sourcePosition.file as? JetFile ?: return null
|
||||
val lineNumber = sourcePosition.line
|
||||
val elementAt = CodeInsightUtils.getTopmostElementAtOffset(
|
||||
sourcePosition.elementAt,
|
||||
file.getLineStartOffset(lineNumber) ?: sourcePosition.elementAt.startOffset
|
||||
) ?: return null
|
||||
|
||||
val start = elementAt.startOffset
|
||||
val end = elementAt.endOffset
|
||||
|
||||
return CodeInsightUtils.
|
||||
findElementsOfClassInRange(file, start, end, JetFunctionLiteral::class.java, JetNamedFunction::class.java)
|
||||
.filter { JetPsiUtil.getParentCallIfPresent(it as JetExpression) != null }
|
||||
.filterIsInstance<JetFunction>()
|
||||
.filter {
|
||||
val context = it.analyze(BodyResolveMode.PARTIAL)
|
||||
InlineUtil.isInlinedArgument(it, context, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,8 +82,7 @@ import org.jetbrains.kotlin.resolve.AnalyzingUtils
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import java.io.File
|
||||
import java.lang.annotation.Retention
|
||||
import java.util.ArrayList
|
||||
import java.util.Collections
|
||||
import java.util.*
|
||||
import javax.swing.Icon
|
||||
|
||||
fun <T: Any> PsiElement.getAndRemoveCopyableUserData(key: Key<T>): T? {
|
||||
@@ -280,6 +279,18 @@ public class SelectionAwareScopeHighlighter(val editor: Editor) {
|
||||
}
|
||||
}
|
||||
|
||||
fun PsiFile.getLineStartOffset(line: Int): Int? {
|
||||
val doc = this.let { file -> PsiDocumentManager.getInstance(getProject()).getDocument(file) }
|
||||
if (doc != null) {
|
||||
val startOffset = doc.getLineStartOffset(line)
|
||||
val element = findElementAt(startOffset) ?: return startOffset
|
||||
|
||||
return PsiTreeUtil.skipSiblingsForward(element, PsiWhiteSpace::class.java, PsiComment::class.java)?.startOffset ?: startOffset
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun PsiElement.getLineCount(): Int {
|
||||
val doc = getContainingFile()?.let { file -> PsiDocumentManager.getInstance(getProject()).getDocument(file) }
|
||||
if (doc != null) {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
LineBreakpoint created at stepOverInlinedLambda.kt:6
|
||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! stepOverInlinedLambda.StepOverInlinedLambdaPackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
stepOverInlinedLambda.kt:6
|
||||
stepOverInlinedLambda.kt:7
|
||||
stepOverInlinedLambda.kt:10
|
||||
stepOverInlinedLambda.kt:11
|
||||
stepOverInlinedLambda.kt:16
|
||||
stepOverInlinedLambda.kt:17
|
||||
stepOverInlinedLambda.kt:19
|
||||
stepOverInlinedLambda.kt:20
|
||||
stepOverInlinedLambda.kt:23
|
||||
stepOverInlinedLambda.kt:29
|
||||
stepOverInlinedLambda.kt:30
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
@@ -0,0 +1,11 @@
|
||||
LineBreakpoint created at stepOverInlinedLambdaStdlib.kt:5
|
||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! stepOverInlinedLambdaStdlib.StepOverInlinedLambdaStdlibPackage
|
||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
stepOverInlinedLambdaStdlib.kt:5
|
||||
stepOverInlinedLambdaStdlib.kt:6
|
||||
stepOverInlinedLambdaStdlib.kt:8
|
||||
stepOverInlinedLambdaStdlib.kt:10
|
||||
stepOverInlinedLambdaStdlib.kt:15
|
||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||
|
||||
Process finished with exit code 0
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package stepOverInlinedLambda
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val a = A()
|
||||
//Breakpoint!
|
||||
foo { test(1) }
|
||||
foo {
|
||||
test(2)
|
||||
}
|
||||
a.foo { test(3) }.foo { test(4) }
|
||||
a.foo {
|
||||
test(5)
|
||||
}.foo {
|
||||
test(6)
|
||||
}
|
||||
a.foo { test(7) }
|
||||
.foo { test(8) }
|
||||
|
||||
foo({ test(9) }) { test(10) }
|
||||
foo({ test(11) }) {
|
||||
test(12)
|
||||
}
|
||||
foo({
|
||||
test(13)
|
||||
}, {
|
||||
test(14)
|
||||
})
|
||||
|
||||
val b = foo { test(1) }
|
||||
}
|
||||
|
||||
inline fun foo(f: () -> Unit) {
|
||||
f()
|
||||
}
|
||||
|
||||
inline fun foo(f1: () -> Unit, f2: () -> Unit) {
|
||||
f1()
|
||||
f2()
|
||||
}
|
||||
|
||||
class A {
|
||||
inline fun foo(f: () -> Unit): A {
|
||||
f()
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
fun test(i: Int) = 1
|
||||
|
||||
// STEP_OVER: 11
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package stepOverInlinedLambdaStdlib
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
//Breakpoint!
|
||||
val a = listOf(1, 2, 3)
|
||||
a.filter { it > 1 }
|
||||
|
||||
a.filter { it > 1 }.map { it * 2 }
|
||||
|
||||
a.filter {
|
||||
it > 1
|
||||
}.map {
|
||||
it * 2
|
||||
}
|
||||
}
|
||||
|
||||
// TRACING_FILTERS_ENABLED: false
|
||||
// STEP_OVER: 4
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.idea.debugger
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils.getPrefixedInt
|
||||
import java.io.File
|
||||
|
||||
@@ -29,6 +30,10 @@ public abstract class AbstractKotlinSteppingTest : KotlinDebuggerTestBase() {
|
||||
doTest(path, "STEP_OUT")
|
||||
}
|
||||
|
||||
protected fun doStepOverTest(path: String) {
|
||||
doTest(path, "STEP_OVER")
|
||||
}
|
||||
|
||||
protected fun doSmartStepIntoTest(path: String) {
|
||||
doTest(path, "SMART_STEP_INTO")
|
||||
}
|
||||
@@ -51,15 +56,9 @@ public abstract class AbstractKotlinSteppingTest : KotlinDebuggerTestBase() {
|
||||
|
||||
createDebugProcess(path)
|
||||
|
||||
for (i in 1..(getPrefixedInt(fileText, "// $command: ") ?: 1)) {
|
||||
doOnBreakpoint {
|
||||
when(command) {
|
||||
"STEP_INTO" -> stepInto(this)
|
||||
"STEP_OUT" -> stepOut()
|
||||
"SMART_STEP_INTO" -> smartStepInto()
|
||||
}
|
||||
}
|
||||
}
|
||||
val prefix = "// $command: "
|
||||
val count = InTextDirectivesUtils.getPrefixedInt(fileText, prefix) ?: "1"
|
||||
processSteppingInstruction("$prefix$count")
|
||||
|
||||
finish()
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import com.intellij.xdebugger.breakpoints.XBreakpoint
|
||||
import com.intellij.xdebugger.breakpoints.XBreakpointProperties
|
||||
import com.intellij.xdebugger.breakpoints.XBreakpointType
|
||||
import com.intellij.xdebugger.breakpoints.XLineBreakpointType
|
||||
import com.sun.jdi.request.StepRequest
|
||||
import org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinFieldBreakpoint
|
||||
import org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinFieldBreakpointType
|
||||
import org.jetbrains.kotlin.idea.debugger.stepping.*
|
||||
@@ -138,24 +139,34 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() {
|
||||
dp.getManagerThread()!!.schedule(dp.createStepOutCommand(this))
|
||||
}
|
||||
|
||||
protected fun SuspendContextImpl.stepOver() {
|
||||
val stepOverCommand = runReadAction { KotlinSteppingCommandProvider().getStepOverCommand(this, false, StepRequest.STEP_LINE) }
|
||||
?: dp.createStepOverCommand(this, false)
|
||||
dp.getManagerThread()!!.schedule(stepOverCommand)
|
||||
}
|
||||
|
||||
protected fun doStepping(path: String) {
|
||||
val file = File(path)
|
||||
val fileText = file.readText()
|
||||
file.readLines().forEach {
|
||||
val line = it.trim()
|
||||
processSteppingInstruction(line)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun processSteppingInstruction(line: String) {
|
||||
fun repeat(indexPrefix: String, f: SuspendContextImpl.() -> Unit) {
|
||||
for (i in 1..(InTextDirectivesUtils.getPrefixedInt(fileText, indexPrefix) ?: 1)) {
|
||||
for (i in 1..(InTextDirectivesUtils.getPrefixedInt(line, indexPrefix) ?: 1)) {
|
||||
doOnBreakpoint(f)
|
||||
}
|
||||
}
|
||||
|
||||
file.readLines().forEach {
|
||||
val line = it.trim()
|
||||
when {
|
||||
line.startsWith("// STEP_INTO: ") -> repeat("// STEP_INTO: ") { stepInto(this) }
|
||||
line.startsWith("// STEP_OUT: ") -> repeat("// STEP_OUT: ") { stepOut() }
|
||||
line.startsWith("// SMART_STEP_INTO_BY_INDEX: ") -> doOnBreakpoint { smartStepInto(InTextDirectivesUtils.getPrefixedInt(it, "// SMART_STEP_INTO_BY_INDEX: ")!!) }
|
||||
line.startsWith("// SMART_STEP_INTO: ") -> repeat("// SMART_STEP_INTO: ") { smartStepInto() }
|
||||
line.startsWith("// RESUME: ") -> repeat("// RESUME: ") { resume(this) }
|
||||
}
|
||||
when {
|
||||
line.startsWith("// STEP_INTO: ") -> repeat("// STEP_INTO: ") { stepInto(this) }
|
||||
line.startsWith("// STEP_OUT: ") -> repeat("// STEP_OUT: ") { stepOut() }
|
||||
line.startsWith("// STEP_OVER: ") -> repeat("// STEP_OVER: ") { stepOver() }
|
||||
line.startsWith("// SMART_STEP_INTO_BY_INDEX: ") -> doOnBreakpoint { smartStepInto(InTextDirectivesUtils.getPrefixedInt(line, "// SMART_STEP_INTO_BY_INDEX: ")!!) }
|
||||
line.startsWith("// SMART_STEP_INTO: ") -> repeat("// SMART_STEP_INTO: ") { smartStepInto() }
|
||||
line.startsWith("// RESUME: ") -> repeat("// RESUME: ") { resume(this) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -216,6 +216,7 @@ public abstract class KotlinDebuggerTestCase extends DescriptorTestCase {
|
||||
|
||||
@SuppressWarnings("MethodMayBeStatic")
|
||||
protected void createDebugProcess(@NotNull String path) throws Exception {
|
||||
VfsUtil.markDirty(true, true, VfsUtil.findFileByIoFile(new File(TINY_APP), true));
|
||||
File file = new File(path);
|
||||
String packageName = file.getName().replace(".kt", "");
|
||||
createLocalProcess(PackageClassUtils.getPackageClassFqName(new FqName(packageName)).asString());
|
||||
|
||||
@@ -299,6 +299,27 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class StepOver extends AbstractKotlinSteppingTest {
|
||||
public void testAllFilesPresentInStepOver() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepping/stepOver"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("stepOverInlinedLambda.kt")
|
||||
public void testStepOverInlinedLambda() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInlinedLambda.kt");
|
||||
doStepOverTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("stepOverInlinedLambdaStdlib.kt")
|
||||
public void testStepOverInlinedLambdaStdlib() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/stepOver/stepOverInlinedLambdaStdlib.kt");
|
||||
doStepOverTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/debugger/tinyApp/src/stepping/filters")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user