1+ /*******************************************************************************
2+ * Copyright (c) 2017 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 .handler ;
13+
14+ import java .util .Arrays ;
15+ import java .util .List ;
16+ import java .util .concurrent .CompletableFuture ;
17+
18+ import com .microsoft .java .debug .core .DebugException ;
19+ import com .microsoft .java .debug .core .DebugUtility ;
20+ import com .microsoft .java .debug .core .StackFrameUtility ;
21+ import com .microsoft .java .debug .core .adapter .AdapterUtils ;
22+ import com .microsoft .java .debug .core .adapter .ErrorCode ;
23+ import com .microsoft .java .debug .core .adapter .IDebugAdapterContext ;
24+ import com .microsoft .java .debug .core .adapter .IDebugRequestHandler ;
25+ import com .microsoft .java .debug .core .adapter .variables .StackFrameReference ;
26+ import com .microsoft .java .debug .core .protocol .Events ;
27+ import com .microsoft .java .debug .core .protocol .Events .UserNotificationEvent .NotificationType ;
28+ import com .microsoft .java .debug .core .protocol .Messages .Response ;
29+ import com .microsoft .java .debug .core .protocol .Requests ;
30+ import com .microsoft .java .debug .core .protocol .Requests .Arguments ;
31+ import com .microsoft .java .debug .core .protocol .Requests .Command ;
32+ import com .microsoft .java .debug .core .protocol .Requests .RestartFrameArguments ;
33+ import com .sun .jdi .StackFrame ;
34+ import com .sun .jdi .ThreadReference ;
35+ import com .sun .jdi .request .StepRequest ;
36+
37+ /**
38+ * Support Eclipse's `Drop To Frame` action, which is restartFrame in VSCode's
39+ * debug.
40+ */
41+ public class RestartFrameHandler implements IDebugRequestHandler {
42+
43+ @ Override
44+ public List <Command > getTargetCommands () {
45+ return Arrays .asList (Requests .Command .RESTARTFRAME );
46+ }
47+
48+ @ Override
49+ public CompletableFuture <Response > handle (Command command , Arguments arguments , Response response , IDebugAdapterContext context ) {
50+ RestartFrameArguments restartFrameArgs = (RestartFrameArguments ) arguments ;
51+ StackFrameReference stackFrameReference = (StackFrameReference ) context .getRecyclableIdPool ().getObjectById (restartFrameArgs .frameId );
52+
53+ if (stackFrameReference == null ) {
54+ return AdapterUtils .createAsyncErrorResponse (response , ErrorCode .RESTARTFRAME_FAILURE ,
55+ String .format ("RestartFrame: cannot find the stack frame with frameID %s" , restartFrameArgs .frameId ));
56+ }
57+
58+ if (canRestartFrame (context , stackFrameReference )) {
59+ try {
60+ ThreadReference reference = stackFrameReference .getThread ();
61+ popStackFrames (context , reference , stackFrameReference .getDepth ());
62+ stepInto (context , reference );
63+ } catch (DebugException de ) {
64+ context .getProtocolServer ().sendEvent (new Events .UserNotificationEvent (NotificationType .ERROR , de .getMessage ()));
65+ }
66+ return CompletableFuture .completedFuture (response );
67+ } else {
68+ context .getProtocolServer ().sendEvent (new Events .UserNotificationEvent (NotificationType .ERROR , "Current stack frame doesn't support restart." ));
69+ context .getProtocolServer ().sendEvent (new Events .StoppedEvent ("restartframe" , stackFrameReference .getThread ().uniqueID ()));
70+ return AdapterUtils .createAsyncErrorResponse (response , ErrorCode .RESTARTFRAME_FAILURE , "Failed to restart the selected stack frame." );
71+ }
72+ }
73+
74+ private boolean canRestartFrame (IDebugAdapterContext context , StackFrameReference frameReference ) {
75+ if (!context .getDebugSession ().getVM ().canPopFrames ()) {
76+ return false ;
77+ }
78+ ThreadReference reference = frameReference .getThread ();
79+ StackFrame [] frames = context .getStackFrameManager ().reloadStackFrames (reference );
80+
81+ // The frame cannot be the bottom one of the call stack:
82+ if (frames .length <= frameReference .getDepth () + 1 ) {
83+ return false ;
84+ }
85+
86+ // Cannot restart frame involved with native call stacks:
87+ for (int i = 0 ; i <= frameReference .getDepth () + 1 ; i ++) {
88+ if (StackFrameUtility .isNative (frames [i ])) {
89+ return false ;
90+ }
91+ }
92+ return true ;
93+ }
94+
95+ private void popStackFrames (IDebugAdapterContext context , ThreadReference thread , int depth ) throws DebugException {
96+ StackFrame [] frames = context .getStackFrameManager ().reloadStackFrames (thread );
97+ StackFrameUtility .pop (frames [depth ]);
98+ }
99+
100+ private void stepInto (IDebugAdapterContext context , ThreadReference thread ) {
101+ StepRequest request = DebugUtility .createStepIntoRequest (thread , context .getStepFilters ().classNameFilters );
102+ context .getDebugSession ().getEventHub ().stepEvents ().filter (debugEvent -> request .equals (debugEvent .event .request ())).take (1 ).subscribe (debugEvent -> {
103+ debugEvent .shouldResume = false ;
104+ // Have to send two events to keep the UI sync with the step in operations:
105+ context .getProtocolServer ().sendEvent (new Events .StoppedEvent ("step" , thread .uniqueID ()));
106+ context .getProtocolServer ().sendEvent (new Events .ContinuedEvent (thread .uniqueID ()));
107+ });
108+ request .enable ();
109+ thread .resume ();
110+ }
111+ }
0 commit comments