1 package org.jetbrains.debugger;
3 import com.intellij.execution.ExecutionResult;
4 import com.intellij.execution.process.ProcessHandler;
5 import com.intellij.openapi.vfs.VirtualFile;
6 import com.intellij.util.Url;
7 import com.intellij.util.containers.ContainerUtil;
8 import com.intellij.util.io.socketConnection.ConnectionStatus;
9 import com.intellij.util.io.socketConnection.SocketConnectionListener;
10 import com.intellij.xdebugger.XDebugProcess;
11 import com.intellij.xdebugger.XDebugSession;
12 import com.intellij.xdebugger.XExpression;
13 import com.intellij.xdebugger.breakpoints.XBreakpoint;
14 import com.intellij.xdebugger.breakpoints.XBreakpointHandler;
15 import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
16 import com.intellij.xdebugger.frame.XSuspendContext;
17 import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
18 import org.jetbrains.annotations.NotNull;
19 import org.jetbrains.annotations.Nullable;
20 import org.jetbrains.debugger.connection.VmConnection;
21 import org.jetbrains.debugger.frame.SuspendContextImpl;
23 import javax.swing.event.HyperlinkListener;
24 import java.util.concurrent.ConcurrentMap;
25 import java.util.concurrent.atomic.AtomicBoolean;
27 public abstract class DebugProcessImpl<C extends VmConnection> extends XDebugProcess {
28 protected final AtomicBoolean repeatStepInto = new AtomicBoolean();
29 protected volatile StepAction lastStep;
30 protected volatile CallFrame lastCallFrame;
31 protected volatile boolean isForceStep;
33 protected final ConcurrentMap<Url, VirtualFile> urlToFileCache = ContainerUtil.newConcurrentMap();
35 protected final C connection;
37 private boolean processBreakpointConditionsAtIdeSide;
39 private final XDebuggerEditorsProvider editorsProvider;
40 private final XSmartStepIntoHandler<?> smartStepIntoHandler;
41 protected XBreakpointHandler<?>[] breakpointHandlers;
43 protected final ExecutionResult executionResult;
45 protected DebugProcessImpl(@NotNull XDebugSession session, @NotNull C connection,
46 @NotNull XDebuggerEditorsProvider editorsProvider,
47 @Nullable XSmartStepIntoHandler<?> smartStepIntoHandler,
48 @Nullable ExecutionResult executionResult) {
51 this.executionResult = executionResult;
52 this.connection = connection;
53 this.editorsProvider = editorsProvider;
54 this.smartStepIntoHandler = smartStepIntoHandler;
56 connection.addListener(new SocketConnectionListener() {
58 public void statusChanged(@NotNull ConnectionStatus status) {
59 if (status == ConnectionStatus.DISCONNECTED || status == ConnectionStatus.DETACHED) {
60 if (status == ConnectionStatus.DETACHED) {
61 ProcessHandler processHandler = doGetProcessHandler();
62 if (processHandler != null) {
63 processHandler.detachProcess();
69 getSession().rebuildViews();
76 public final C getConnection() {
82 public final XSmartStepIntoHandler<?> getSmartStepIntoHandler() {
83 return smartStepIntoHandler;
88 public final XBreakpointHandler<?>[] getBreakpointHandlers() {
89 return breakpointHandlers;
94 public final XDebuggerEditorsProvider getEditorsProvider() {
95 return editorsProvider;
98 public void setProcessBreakpointConditionsAtIdeSide(final boolean processBreakpointConditionsAtIdeSide) {
99 this.processBreakpointConditionsAtIdeSide = processBreakpointConditionsAtIdeSide;
102 public final Vm getVm() {
103 return connection.getVm();
106 private void updateLastCallFrame() {
109 SuspendContext context = vm.getSuspendContextManager().getContext();
110 if (context != null) {
111 lastCallFrame = context.getTopFrame();
116 lastCallFrame = null;
120 public boolean checkCanPerformCommands() {
121 return getVm() != null;
125 public boolean isValuesCustomSorted() {
130 public void startStepOver() {
131 updateLastCallFrame();
132 continueVm(StepAction.OVER);
136 public void startForceStepInto() {
142 public void startStepInto() {
143 updateLastCallFrame();
144 continueVm(StepAction.IN);
148 public void startStepOut() {
149 if (isVmStepOutCorrect()) {
150 lastCallFrame = null;
153 updateLastCallFrame();
155 continueVm(StepAction.OUT);
158 // some VM (firefox for example) doesn't implement step out correctly, so, we need to fix it
159 protected boolean isVmStepOutCorrect() {
163 protected void continueVm(@NotNull StepAction stepAction) {
164 SuspendContextManager suspendContextManager = getVm().getSuspendContextManager();
165 if (stepAction == StepAction.CONTINUE) {
166 if (suspendContextManager.getContext() == null) {
167 // on resumed we ask session to resume, and session then call our "resume", but we have already resumed, so, we don't need to send "continue" message
172 lastCallFrame = null;
173 urlToFileCache.clear();
176 lastStep = stepAction;
178 suspendContextManager.continueVm(stepAction, 1);
181 protected final void setOverlay() {
182 getVm().getSuspendContextManager().setOverlayMessage("Paused in debugger");
185 protected final void processBreakpoint(@NotNull final SuspendContext suspendContext,
186 @NotNull final XBreakpoint<?> breakpoint,
187 @NotNull final SuspendContextImpl xSuspendContext) {
188 XExpression conditionExpression = breakpoint.getConditionExpression();
189 String condition = conditionExpression == null ? null : conditionExpression.getExpression();
190 if (!processBreakpointConditionsAtIdeSide || condition == null) {
191 processBreakpointLogExpressionAndSuspend(breakpoint, xSuspendContext, suspendContext);
194 xSuspendContext.evaluateExpression(condition).done(new ContextDependentAsyncResultConsumer<String>(suspendContext) {
196 public void consume(String evaluationResult, @NotNull Vm vm) {
197 if ("false".equals(evaluationResult)) {
201 processBreakpointLogExpressionAndSuspend(breakpoint, xSuspendContext, suspendContext);
204 }).rejected(new ContextDependentAsyncResultConsumer<Throwable>(suspendContext) {
206 public void consume(Throwable failure, @NotNull Vm vm) {
207 processBreakpointLogExpressionAndSuspend(breakpoint, xSuspendContext, suspendContext);
213 private void processBreakpointLogExpressionAndSuspend(@NotNull final XBreakpoint<?> breakpoint,
214 @NotNull final SuspendContextImpl xSuspendContext,
215 @NotNull SuspendContext suspendContext) {
216 XExpression logExpressionObject = breakpoint.getLogExpressionObject();
217 final String logExpression = logExpressionObject == null ? null : logExpressionObject.getExpression();
219 if (logExpression == null) {
220 breakpointReached(breakpoint, null, xSuspendContext);
223 xSuspendContext.evaluateExpression(logExpression).done(new ContextDependentAsyncResultConsumer<String>(suspendContext) {
225 public void consume(String logResult, @NotNull Vm vm) {
226 breakpointReached(breakpoint, logResult, xSuspendContext);
228 }).rejected(new ContextDependentAsyncResultConsumer<Throwable>(suspendContext) {
230 public void consume(Throwable logResult, @NotNull Vm vm) {
231 breakpointReached(breakpoint, "Failed to evaluate expression: " + logExpression, xSuspendContext);
237 private void breakpointReached(@NotNull XBreakpoint<?> breakpoint,
238 @Nullable String evaluatedLogExpression,
239 @NotNull XSuspendContext suspendContext) {
240 if (getSession().breakpointReached(breakpoint, evaluatedLogExpression, suspendContext)) {
249 public final void startPausing() {
250 connection.getVm().getSuspendContextManager().suspend().rejected(new RejectErrorReporter(getSession(), "Cannot pause"));
254 public final String getCurrentStateMessage() {
255 return connection.getState().getMessage();
260 public final HyperlinkListener getCurrentStateHyperlinkListener() {
261 return getConnection().getState().getMessageLinkListener();
265 protected final ProcessHandler doGetProcessHandler() {
266 return executionResult != null ? executionResult.getProcessHandler() : null;