Skip to content

Commit bf689a6

Browse files
Display the method return value when stepping out (#336)
* Show the method return value when stepping out Signed-off-by: Jinbo Wang <[email protected]> * Clear the cached method results when pause/continue current thread Signed-off-by: Jinbo Wang <[email protected]> * Add an icon to method return value Signed-off-by: Jinbo Wang <[email protected]> * Refine the key for the return value Signed-off-by: Jinbo Wang <[email protected]> * Handle the emoji display support issue for different platform Signed-off-by: Jinbo Wang <[email protected]>
1 parent 73b448d commit bf689a6

File tree

9 files changed

+175
-12
lines changed

9 files changed

+175
-12
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2020 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Microsoft Corporation - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.microsoft.java.debug.core;
13+
14+
import com.sun.jdi.Method;
15+
import com.sun.jdi.Value;
16+
17+
public class JdiMethodResult {
18+
public Method method;
19+
public Value value;
20+
21+
public JdiMethodResult(Method method, Value value) {
22+
this.method = method;
23+
this.value = value;
24+
}
25+
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/AdapterUtils.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
public class AdapterUtils {
5252
private static final String OS_NAME = System.getProperty("os.name", "").toLowerCase();
5353
private static final Pattern ENCLOSING_CLASS_REGEX = Pattern.compile("^([^\\$]*)");
54+
public static final boolean isWin = isWindows();
55+
public static final boolean isMac = OS_NAME.contains("mac") || OS_NAME.contains("darwin");
5456

5557
/**
5658
* Check if the OS is windows or not.

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/DebugAdapterContext.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2017 Microsoft Corporation and others.
2+
* Copyright (c) 2017-2020 Microsoft Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -61,6 +61,7 @@ public class DebugAdapterContext implements IDebugAdapterContext {
6161
private IStackFrameManager stackFrameManager = new StackFrameManager();
6262
private IExceptionManager exceptionManager = new ExceptionManager();
6363
private IBreakpointManager breakpointManager = new BreakpointManager();
64+
private IStepResultManager stepResultManager = new StepResultManager();
6465

6566
public DebugAdapterContext(IProtocolServer server, IProviderContext providerContext) {
6667
this.providerContext = providerContext;
@@ -320,4 +321,9 @@ public IExceptionManager getExceptionManager() {
320321
public IBreakpointManager getBreakpointManager() {
321322
return breakpointManager;
322323
}
324+
325+
@Override
326+
public IStepResultManager getStepResultManager() {
327+
return stepResultManager;
328+
}
323329
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/IDebugAdapterContext.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2017 Microsoft Corporation and others.
2+
* Copyright (c) 2017-2020 Microsoft Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -126,4 +126,6 @@ public interface IDebugAdapterContext {
126126
IExceptionManager getExceptionManager();
127127

128128
IBreakpointManager getBreakpointManager();
129+
130+
IStepResultManager getStepResultManager();
129131
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2020 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Microsoft Corporation - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.microsoft.java.debug.core.adapter;
13+
14+
import com.microsoft.java.debug.core.JdiMethodResult;
15+
16+
public interface IStepResultManager {
17+
JdiMethodResult setMethodResult(long threadId, JdiMethodResult methodResult);
18+
19+
JdiMethodResult getMethodResult(long threadId);
20+
21+
JdiMethodResult removeMethodResult(long threadId);
22+
23+
void removeAllMethodResults();
24+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2020 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Microsoft Corporation - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.microsoft.java.debug.core.adapter;
13+
14+
import java.util.Collections;
15+
import java.util.HashMap;
16+
import java.util.Map;
17+
18+
import com.microsoft.java.debug.core.JdiMethodResult;
19+
20+
public class StepResultManager implements IStepResultManager {
21+
private Map<Long, JdiMethodResult> methodResults = Collections.synchronizedMap(new HashMap<>());
22+
23+
@Override
24+
public JdiMethodResult setMethodResult(long threadId, JdiMethodResult methodResult) {
25+
return this.methodResults.put(threadId, methodResult);
26+
}
27+
28+
@Override
29+
public JdiMethodResult getMethodResult(long threadId) {
30+
return this.methodResults.get(threadId);
31+
}
32+
33+
@Override
34+
public JdiMethodResult removeMethodResult(long threadId) {
35+
return this.methodResults.remove(threadId);
36+
}
37+
38+
@Override
39+
public void removeAllMethodResults() {
40+
this.methodResults.clear();
41+
}
42+
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/StepRequestHandler.java

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2017 Microsoft Corporation and others.
2+
* Copyright (c) 2017-2020 Microsoft Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -21,6 +21,7 @@
2121
import com.microsoft.java.debug.core.DebugUtility;
2222
import com.microsoft.java.debug.core.IDebugSession;
2323
import com.microsoft.java.debug.core.JdiExceptionReference;
24+
import com.microsoft.java.debug.core.JdiMethodResult;
2425
import com.microsoft.java.debug.core.adapter.AdapterUtils;
2526
import com.microsoft.java.debug.core.adapter.ErrorCode;
2627
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
@@ -34,11 +35,20 @@
3435
import com.sun.jdi.IncompatibleThreadStateException;
3536
import com.sun.jdi.Location;
3637
import com.sun.jdi.Method;
38+
import com.sun.jdi.ObjectReference;
3739
import com.sun.jdi.StackFrame;
3840
import com.sun.jdi.ThreadReference;
41+
import com.sun.jdi.Value;
42+
import com.sun.jdi.VoidValue;
3943
import com.sun.jdi.event.BreakpointEvent;
4044
import com.sun.jdi.event.Event;
45+
import com.sun.jdi.event.ExceptionEvent;
46+
import com.sun.jdi.event.LocatableEvent;
47+
import com.sun.jdi.event.MethodExitEvent;
4148
import com.sun.jdi.event.StepEvent;
49+
import com.sun.jdi.request.EventRequest;
50+
import com.sun.jdi.request.EventRequestManager;
51+
import com.sun.jdi.request.MethodExitRequest;
4252
import com.sun.jdi.request.StepRequest;
4353

4454
import io.reactivex.disposables.Disposable;
@@ -61,6 +71,7 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
6171
ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), threadId);
6272
if (thread != null) {
6373
JdiExceptionReference exception = context.getExceptionManager().removeException(threadId);
74+
context.getStepResultManager().removeMethodResult(threadId);
6475
try {
6576
ThreadState threadState = new ThreadState();
6677
threadState.threadId = threadId;
@@ -69,7 +80,9 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
6980
threadState.stepLocation = getTopFrame(thread).location();
7081
threadState.eventSubscription = context.getDebugSession().getEventHub().events()
7182
.filter(debugEvent -> (debugEvent.event instanceof StepEvent && debugEvent.event.request().equals(threadState.pendingStepRequest))
72-
|| debugEvent.event instanceof BreakpointEvent)
83+
|| (debugEvent.event instanceof MethodExitEvent && debugEvent.event.request().equals(threadState.pendingMethodExitRequest))
84+
|| debugEvent.event instanceof BreakpointEvent
85+
|| debugEvent.event instanceof ExceptionEvent)
7386
.subscribe(debugEvent -> {
7487
handleDebugEvent(debugEvent, context.getDebugSession(), context, threadState);
7588
});
@@ -86,6 +99,24 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
8699
threadState.pendingStepRequest = DebugUtility.createStepOverRequest(thread, null);
87100
}
88101
threadState.pendingStepRequest.enable();
102+
103+
MethodExitRequest methodExitRequest = thread.virtualMachine().eventRequestManager().createMethodExitRequest();
104+
methodExitRequest.addThreadFilter(thread);
105+
methodExitRequest.addClassFilter(threadState.stepLocation.declaringType());
106+
if (thread.virtualMachine().canUseInstanceFilters()) {
107+
try {
108+
ObjectReference thisObject = getTopFrame(thread).thisObject();
109+
if (thisObject != null) {
110+
methodExitRequest.addInstanceFilter(thisObject);
111+
}
112+
} catch (Exception e) {
113+
// ignore
114+
}
115+
}
116+
methodExitRequest.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
117+
threadState.pendingMethodExitRequest = methodExitRequest;
118+
methodExitRequest.enable();
119+
89120
DebugUtility.resumeThread(thread);
90121

91122
ThreadsRequestHandler.checkThreadRunningAndRecycleIds(thread, context);
@@ -116,19 +147,18 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession,
116147
Event event = debugEvent.event;
117148

118149
// When a breakpoint occurs, abort any pending step requests from the same thread.
119-
if (event instanceof BreakpointEvent) {
120-
long threadId = ((BreakpointEvent) event).thread().uniqueID();
150+
if (event instanceof BreakpointEvent || event instanceof ExceptionEvent) {
151+
long threadId = ((LocatableEvent) event).thread().uniqueID();
121152
if (threadId == threadState.threadId && threadState.pendingStepRequest != null) {
122-
DebugUtility.deleteEventRequestSafely(debugSession.getVM().eventRequestManager(), threadState.pendingStepRequest);
123-
threadState.pendingStepRequest = null;
153+
threadState.deleteStepRequests(debugSession.getVM().eventRequestManager());
154+
context.getStepResultManager().removeMethodResult(threadId);
124155
if (threadState.eventSubscription != null) {
125156
threadState.eventSubscription.dispose();
126157
}
127158
}
128159
} else if (event instanceof StepEvent) {
129160
ThreadReference thread = ((StepEvent) event).thread();
130-
DebugUtility.deleteEventRequestSafely(thread.virtualMachine().eventRequestManager(), threadState.pendingStepRequest);
131-
threadState.pendingStepRequest = null;
161+
threadState.deleteStepRequests(debugSession.getVM().eventRequestManager());
132162
if (isStepFiltersConfigured(context.getStepFilters())) {
133163
try {
134164
if (threadState.pendingStepType == Command.STEPIN) {
@@ -156,6 +186,19 @@ private void handleDebugEvent(DebugEvent debugEvent, IDebugSession debugSession,
156186
}
157187
context.getProtocolServer().sendEvent(new Events.StoppedEvent("step", thread.uniqueID()));
158188
debugEvent.shouldResume = false;
189+
} else if (event instanceof MethodExitEvent) {
190+
MethodExitEvent methodExitEvent = (MethodExitEvent) event;
191+
long threadId = methodExitEvent.thread().uniqueID();
192+
if (threadId == threadState.threadId && methodExitEvent.method().equals(threadState.stepLocation.method())) {
193+
Value returnValue = methodExitEvent.returnValue();
194+
if (returnValue instanceof VoidValue) {
195+
context.getStepResultManager().removeMethodResult(threadId);
196+
} else {
197+
JdiMethodResult methodResult = new JdiMethodResult(methodExitEvent.method(), returnValue);
198+
context.getStepResultManager().setMethodResult(threadId, methodResult);
199+
}
200+
}
201+
debugEvent.shouldResume = true;
159202
}
160203
}
161204

@@ -232,8 +275,16 @@ class ThreadState {
232275
long threadId = -1;
233276
Command pendingStepType;
234277
StepRequest pendingStepRequest = null;
278+
MethodExitRequest pendingMethodExitRequest = null;
235279
int stackDepth = -1;
236280
Location stepLocation = null;
237281
Disposable eventSubscription = null;
282+
283+
public void deleteStepRequests(EventRequestManager manager) {
284+
DebugUtility.deleteEventRequestSafely(manager, this.pendingStepRequest);
285+
DebugUtility.deleteEventRequestSafely(manager, this.pendingMethodExitRequest);
286+
this.pendingMethodExitRequest = null;
287+
this.pendingStepRequest = null;
288+
}
238289
}
239290
}

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/ThreadsRequestHandler.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,11 @@ private CompletableFuture<Response> threads(Requests.ThreadsArguments arguments,
9595
private CompletableFuture<Response> pause(Requests.PauseArguments arguments, Response response, IDebugAdapterContext context) {
9696
ThreadReference thread = DebugUtility.getThread(context.getDebugSession(), arguments.threadId);
9797
if (thread != null) {
98+
context.getStepResultManager().removeMethodResult(arguments.threadId);
9899
thread.suspend();
99100
context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId));
100101
} else {
102+
context.getStepResultManager().removeAllMethodResults();
101103
context.getDebugSession().suspend();
102104
context.getProtocolServer().sendEvent(new Events.StoppedEvent("pause", arguments.threadId, true));
103105
}
@@ -113,11 +115,13 @@ private CompletableFuture<Response> resume(Requests.ContinueArguments arguments,
113115
* be resumed (through ThreadReference#resume() or VirtualMachine#resume()) the same number of times it has been suspended.
114116
*/
115117
if (thread != null) {
118+
context.getStepResultManager().removeMethodResult(arguments.threadId);
116119
context.getExceptionManager().removeException(arguments.threadId);
117120
allThreadsContinued = false;
118121
DebugUtility.resumeThread(thread);
119122
checkThreadRunningAndRecycleIds(thread, context);
120123
} else {
124+
context.getStepResultManager().removeAllMethodResults();
121125
context.getExceptionManager().removeAllExceptions();
122126
context.getDebugSession().resume();
123127
context.getRecyclableIdPool().removeAllObjects();

com.microsoft.java.debug.core/src/main/java/com/microsoft/java/debug/core/adapter/handler/VariablesRequestHandler.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2017-2019 Microsoft Corporation and others.
2+
* Copyright (c) 2017-2020 Microsoft Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -28,6 +28,7 @@
2828

2929
import com.microsoft.java.debug.core.Configuration;
3030
import com.microsoft.java.debug.core.DebugSettings;
31+
import com.microsoft.java.debug.core.JdiMethodResult;
3132
import com.microsoft.java.debug.core.adapter.AdapterUtils;
3233
import com.microsoft.java.debug.core.adapter.ErrorCode;
3334
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
@@ -106,7 +107,13 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
106107
ErrorCode.GET_VARIABLE_FAILURE);
107108
}
108109
try {
109-
childrenList = VariableUtils.listLocalVariables(frame);
110+
long threadId = stackFrameReference.getThread().uniqueID();
111+
JdiMethodResult result = context.getStepResultManager().getMethodResult(threadId);
112+
if (result != null) {
113+
String returnIcon = (AdapterUtils.isWin || AdapterUtils.isMac) ? "⎯►" : "->";
114+
childrenList.add(new Variable(returnIcon + result.method.name() + "()", result.value));
115+
}
116+
childrenList.addAll(VariableUtils.listLocalVariables(frame));
110117
Variable thisVariable = VariableUtils.getThisVariable(frame);
111118
if (thisVariable != null) {
112119
childrenList.add(thisVariable);

0 commit comments

Comments
 (0)