Debugger: fix step over for inline argument with non-local return
This commit is contained in:
@@ -14,6 +14,7 @@
|
|||||||
#### Debugger
|
#### Debugger
|
||||||
|
|
||||||
- Do not step into inline lambda argument during step over inside inline function body
|
- 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
|
## 1.0.2
|
||||||
|
|
||||||
|
|||||||
@@ -19,16 +19,33 @@ package org.jetbrains.kotlin.idea.debugger.stepping;
|
|||||||
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.evaluation.EvaluateException;
|
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||||
|
import com.intellij.debugger.impl.DebuggerUtilsEx;
|
||||||
import com.intellij.debugger.jdi.StackFrameProxyImpl;
|
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.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 kotlin.ranges.IntRange;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.psi.KtFile;
|
import org.jetbrains.kotlin.psi.KtFile;
|
||||||
import org.jetbrains.kotlin.psi.KtFunction;
|
import org.jetbrains.kotlin.psi.KtFunction;
|
||||||
import org.jetbrains.kotlin.psi.KtFunctionLiteral;
|
import org.jetbrains.kotlin.psi.KtFunctionLiteral;
|
||||||
import org.jetbrains.kotlin.psi.KtNamedFunction;
|
import org.jetbrains.kotlin.psi.KtNamedFunction;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
public class DebuggerSteppingHelper {
|
public class DebuggerSteppingHelper {
|
||||||
|
|
||||||
public static DebugProcessImpl.ResumeCommand createStepOverCommand(
|
public static DebugProcessImpl.ResumeCommand createStepOverCommand(
|
||||||
@@ -57,6 +74,11 @@ public class DebuggerSteppingHelper {
|
|||||||
DebugProcessImpl.ResumeCommand command = action.createCommand(debugProcess, suspendContext, ignoreBreakpoints);
|
DebugProcessImpl.ResumeCommand command = action.createCommand(debugProcess, suspendContext, ignoreBreakpoints);
|
||||||
|
|
||||||
if (command != null) {
|
if (command != null) {
|
||||||
|
createStepRequest(
|
||||||
|
suspendContext, getContextThread(),
|
||||||
|
debugProcess.getVirtualMachineProxy().eventRequestManager(),
|
||||||
|
StepRequest.STEP_LINE, StepRequest.STEP_OUT);
|
||||||
|
|
||||||
command.contextAction();
|
command.contextAction();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -93,6 +115,11 @@ public class DebuggerSteppingHelper {
|
|||||||
DebugProcessImpl.ResumeCommand command = action.createCommand(debugProcess, suspendContext, ignoreBreakpoints);
|
DebugProcessImpl.ResumeCommand command = action.createCommand(debugProcess, suspendContext, ignoreBreakpoints);
|
||||||
|
|
||||||
if (command != null) {
|
if (command != null) {
|
||||||
|
createStepRequest(
|
||||||
|
suspendContext, getContextThread(),
|
||||||
|
debugProcess.getVirtualMachineProxy().eventRequestManager(),
|
||||||
|
StepRequest.STEP_LINE, StepRequest.STEP_OUT);
|
||||||
|
|
||||||
command.contextAction();
|
command.contextAction();
|
||||||
return;
|
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<ClassFilter> 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<ClassFilter> getActiveFilters() {
|
||||||
|
List<ClassFilter> activeFilters = new ArrayList<ClassFilter>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
package stepOverNonLocalReturnInLambda
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -655,6 +655,12 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest {
|
|||||||
doCustomTest(fileName);
|
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")
|
@TestMetadata("syntheticProvider.kt")
|
||||||
public void testSyntheticProvider() throws Exception {
|
public void testSyntheticProvider() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/syntheticProvider.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepping/custom/syntheticProvider.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user