diff --git a/ChangeLog.md b/ChangeLog.md index 34ac51409e1..8f4886e70f0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -14,6 +14,7 @@ #### Debugger - Do not step into inline lambda argument during step over inside inline function body +- Fix step over for inline argument with non-local return ## 1.0.2 diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebuggerSteppingHelper.java b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebuggerSteppingHelper.java index 8112a91089c..6d6a04daf61 100644 --- a/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebuggerSteppingHelper.java +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/stepping/DebuggerSteppingHelper.java @@ -19,16 +19,33 @@ package org.jetbrains.kotlin.idea.debugger.stepping; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.debugger.jdi.StackFrameProxyImpl; +import com.intellij.debugger.jdi.ThreadReferenceProxyImpl; +import com.intellij.debugger.settings.DebuggerSettings; +import com.intellij.openapi.extensions.Extensions; import com.intellij.psi.PsiElement; +import com.intellij.ui.classFilter.ClassFilter; +import com.intellij.ui.classFilter.DebuggerClassFilterProvider; +import com.sun.jdi.Location; +import com.sun.jdi.ObjectCollectedException; +import com.sun.jdi.ReferenceType; +import com.sun.jdi.ThreadReference; +import com.sun.jdi.request.EventRequest; +import com.sun.jdi.request.EventRequestManager; +import com.sun.jdi.request.StepRequest; import kotlin.ranges.IntRange; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtFunction; import org.jetbrains.kotlin.psi.KtFunctionLiteral; import org.jetbrains.kotlin.psi.KtNamedFunction; +import java.util.ArrayList; import java.util.List; + public class DebuggerSteppingHelper { public static DebugProcessImpl.ResumeCommand createStepOverCommand( @@ -57,6 +74,11 @@ public class DebuggerSteppingHelper { DebugProcessImpl.ResumeCommand command = action.createCommand(debugProcess, suspendContext, ignoreBreakpoints); if (command != null) { + createStepRequest( + suspendContext, getContextThread(), + debugProcess.getVirtualMachineProxy().eventRequestManager(), + StepRequest.STEP_LINE, StepRequest.STEP_OUT); + command.contextAction(); return; } @@ -93,6 +115,11 @@ public class DebuggerSteppingHelper { DebugProcessImpl.ResumeCommand command = action.createCommand(debugProcess, suspendContext, ignoreBreakpoints); if (command != null) { + createStepRequest( + suspendContext, getContextThread(), + debugProcess.getVirtualMachineProxy().eventRequestManager(), + StepRequest.STEP_LINE, StepRequest.STEP_OUT); + command.contextAction(); return; } @@ -106,4 +133,89 @@ public class DebuggerSteppingHelper { } }; } + + // copied from DebugProcessImpl.doStep + private static void createStepRequest( + @NotNull SuspendContextImpl suspendContext, + @Nullable ThreadReferenceProxyImpl stepThread, + @NotNull EventRequestManager requestManager, + int size, int depth + ) { + if (stepThread == null) { + return; + } + try { + ThreadReference stepThreadReference = stepThread.getThreadReference(); + + requestManager.deleteEventRequests(requestManager.stepRequests()); + + StepRequest stepRequest = requestManager.createStepRequest(stepThreadReference, size, depth); + + List activeFilters = getActiveFilters(); + + if (!activeFilters.isEmpty()) { + String currentClassName = getCurrentClassName(stepThread); + if (currentClassName == null || !DebuggerUtilsEx.isFiltered(currentClassName, activeFilters)) { + // add class filters + for (ClassFilter filter : activeFilters) { + stepRequest.addClassExclusionFilter(filter.getPattern()); + } + } + } + + // suspend policy to match the suspend policy of the context: + // if all threads were suspended, then during stepping all the threads must be suspended + // if only event thread were suspended, then only this particular thread must be suspended during stepping + stepRequest.setSuspendPolicy(suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD + ? EventRequest.SUSPEND_EVENT_THREAD + : EventRequest.SUSPEND_ALL); + + stepRequest.enable(); + } + catch (ObjectCollectedException ignored) { + + } + } + + // copied from DebugProcessImpl.getActiveFilters + @NotNull + private static List getActiveFilters() { + List activeFilters = new ArrayList(); + DebuggerSettings settings = DebuggerSettings.getInstance(); + if (settings.TRACING_FILTERS_ENABLED) { + for (ClassFilter filter : settings.getSteppingFilters()) { + if (filter.isEnabled()) { + activeFilters.add(filter); + } + } + } + for (DebuggerClassFilterProvider provider : Extensions.getExtensions(DebuggerClassFilterProvider.EP_NAME)) { + for (ClassFilter filter : provider.getFilters()) { + if (filter.isEnabled()) { + activeFilters.add(filter); + } + } + } + return activeFilters; + } + + // copied from DebugProcessImpl.getActiveFilters + @Nullable + private static String getCurrentClassName(ThreadReferenceProxyImpl thread) { + try { + if (thread != null && thread.frameCount() > 0) { + StackFrameProxyImpl stackFrame = thread.frame(0); + if (stackFrame != null) { + Location location = stackFrame.location(); + ReferenceType referenceType = location == null ? null : location.declaringType(); + if (referenceType != null) { + return referenceType.name(); + } + } + } + } + catch (EvaluateException ignored) { + } + return null; + } } diff --git a/idea/testData/debugger/tinyApp/outs/stepOverNonLocalReturnInLambda.out b/idea/testData/debugger/tinyApp/outs/stepOverNonLocalReturnInLambda.out new file mode 100644 index 00000000000..2aa89d67073 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/stepOverNonLocalReturnInLambda.out @@ -0,0 +1,17 @@ +LineBreakpoint created at stepOverNonLocalReturnInLambda.kt:20 +LineBreakpoint created at stepOverNonLocalReturnInLambda.kt:34 +LineBreakpoint created at stepOverNonLocalReturnInLambda.kt:52 +!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! stepOverNonLocalReturnInLambda.StepOverNonLocalReturnInLambdaKt +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +stepOverNonLocalReturnInLambda.kt:20 +stepOverNonLocalReturnInLambda.kt:21 +stepOverNonLocalReturnInLambda.kt:27 +stepOverNonLocalReturnInLambda.kt:34 +stepOverNonLocalReturnInLambda.kt:35 +stepOverNonLocalReturnInLambda.kt:7 +stepOverNonLocalReturnInLambda.kt:52 +stepOverNonLocalReturnInLambda.kt:53 +stepOverNonLocalReturnInLambda.kt:10 +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/stepOverNonLocalReturnInLambda.kt b/idea/testData/debugger/tinyApp/src/stepping/custom/stepOverNonLocalReturnInLambda.kt new file mode 100644 index 00000000000..4fb7231d224 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepping/custom/stepOverNonLocalReturnInLambda.kt @@ -0,0 +1,56 @@ +package stepOverNonLocalReturnInLambda + +fun main(args: Array) { + try { + test1() + test2() + test3() + } + catch(e: Exception) { + val a = 1 + } + + val c = 1 +} + +fun test1() { + // STEP_OVER: 2 + // RESUME: 1 + //Breakpoint! + val a = "aaa" + synchronized(a) { + if (a == "bbb") { + return + } + } + + val c = 1 +} + +fun test2() { + // STEP_OVER: 2 + // RESUME: 1 + //Breakpoint! + val a = "aaa" + synchronized(a) { + if (a == "aaa") { + return + } + } + + val c = 1 +} + +private fun test3() { + inlineFunThrowException() +} + +inline fun inlineFunThrowException() { + // STEP_OVER: 2 + // RESUME: 1 + //Breakpoint! + val a = 1 + synchronized(a) { + throw IllegalArgumentException() + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java index 524a4623a6f..23ed8f5ecd3 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java @@ -655,6 +655,12 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { doCustomTest(fileName); } + @TestMetadata("stepOverNonLocalReturnInLambda.kt") + public void testStepOverNonLocalReturnInLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/stepOverNonLocalReturnInLambda.kt"); + doCustomTest(fileName); + } + @TestMetadata("syntheticProvider.kt") public void testSyntheticProvider() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/syntheticProvider.kt");