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.DefaultDebugProcessHandler;
11 import com.intellij.xdebugger.XDebugProcess;
12 import com.intellij.xdebugger.XDebugSession;
13 import com.intellij.xdebugger.XExpression;
14 import com.intellij.xdebugger.breakpoints.XBreakpoint;
15 import com.intellij.xdebugger.breakpoints.XBreakpointHandler;
16 import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
17 import com.intellij.xdebugger.frame.XSuspendContext;
18 import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
19 import org.jetbrains.annotations.NotNull;
20 import org.jetbrains.annotations.Nullable;
21 import org.jetbrains.debugger.connection.VmConnection;
22 import org.jetbrains.debugger.frame.SuspendContextImpl;
24 import javax.swing.event.HyperlinkListener;
25 import java.util.concurrent.ConcurrentMap;
26 import java.util.concurrent.atomic.AtomicBoolean;
28 public abstract class DebugProcessImpl<C extends VmConnection> extends XDebugProcess {
29 protected final AtomicBoolean repeatStepInto = new AtomicBoolean();
30 protected volatile StepAction lastStep;
31 protected volatile CallFrame lastCallFrame;
32 protected volatile boolean isForceStep;
34 protected final ConcurrentMap<Url, VirtualFile> urlToFileCache = ContainerUtil.newConcurrentMap();
36 protected final C connection;
38 private boolean processBreakpointConditionsAtIdeSide;
40 private final XDebuggerEditorsProvider editorsProvider;
41 private final XSmartStepIntoHandler<?> smartStepIntoHandler;
42 protected XBreakpointHandler<?>[] breakpointHandlers;
44 protected final ExecutionResult executionResult;
46 protected DebugProcessImpl(@NotNull XDebugSession session, @NotNull C connection,
47 @NotNull XDebuggerEditorsProvider editorsProvider,
48 @Nullable XSmartStepIntoHandler<?> smartStepIntoHandler,
49 @Nullable ExecutionResult executionResult) {
52 this.executionResult = executionResult;
53 this.connection = connection;
54 this.editorsProvider = editorsProvider;
55 this.smartStepIntoHandler = smartStepIntoHandler;
57 connection.addListener(new SocketConnectionListener() {
59 public void statusChanged(@NotNull ConnectionStatus status) {
60 if (status == ConnectionStatus.DISCONNECTED || status == ConnectionStatus.DETACHED) {
61 if (status == ConnectionStatus.DETACHED) {
62 ProcessHandler processHandler = doGetProcessHandler();
63 if (processHandler != null) {
64 processHandler.detachProcess();
70 getSession().rebuildViews();
77 public final C getConnection() {
83 public final XSmartStepIntoHandler<?> getSmartStepIntoHandler() {
84 return smartStepIntoHandler;
89 public final XBreakpointHandler<?>[] getBreakpointHandlers() {
90 return breakpointHandlers;
95 public final XDebuggerEditorsProvider getEditorsProvider() {
96 return editorsProvider;
99 public void setProcessBreakpointConditionsAtIdeSide(final boolean processBreakpointConditionsAtIdeSide) {
100 this.processBreakpointConditionsAtIdeSide = processBreakpointConditionsAtIdeSide;
103 public final Vm getVm() {
104 return connection.getVm();
107 private void updateLastCallFrame() {
110 SuspendContext context = vm.getSuspendContextManager().getContext();
111 if (context != null) {
112 lastCallFrame = context.getTopFrame();
117 lastCallFrame = null;
121 public boolean checkCanPerformCommands() {
122 return getVm() != null;
126 public boolean isValuesCustomSorted() {
131 public void startStepOver() {
132 updateLastCallFrame();
133 continueVm(StepAction.OVER);
137 public void startForceStepInto() {
143 public void startStepInto() {
144 updateLastCallFrame();
145 continueVm(StepAction.IN);
149 public void startStepOut() {
150 if (isVmStepOutCorrect()) {
151 lastCallFrame = null;
154 updateLastCallFrame();
156 continueVm(StepAction.OUT);
159 // some VM (firefox for example) doesn't implement step out correctly, so, we need to fix it
160 protected boolean isVmStepOutCorrect() {
164 protected void continueVm(@NotNull StepAction stepAction) {
165 SuspendContextManager suspendContextManager = getVm().getSuspendContextManager();
166 if (stepAction == StepAction.CONTINUE) {
167 if (suspendContextManager.getContext() == null) {
168 // 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
173 lastCallFrame = null;
174 urlToFileCache.clear();
177 lastStep = stepAction;
179 suspendContextManager.continueVm(stepAction, 1);
182 protected final void setOverlay() {
183 getVm().getSuspendContextManager().setOverlayMessage("Paused in debugger");
186 protected final void processBreakpoint(@NotNull final SuspendContext suspendContext,
187 @NotNull final XBreakpoint<?> breakpoint,
188 @NotNull final SuspendContextImpl xSuspendContext) {
189 XExpression conditionExpression = breakpoint.getConditionExpression();
190 String condition = conditionExpression == null ? null : conditionExpression.getExpression();
191 if (!processBreakpointConditionsAtIdeSide || condition == null) {
192 processBreakpointLogExpressionAndSuspend(breakpoint, xSuspendContext, suspendContext);
195 xSuspendContext.evaluateExpression(condition).done(new ContextDependentAsyncResultConsumer<String>(suspendContext) {
197 public void consume(String evaluationResult, @NotNull Vm vm) {
198 if ("false".equals(evaluationResult)) {
202 processBreakpointLogExpressionAndSuspend(breakpoint, xSuspendContext, suspendContext);
205 }).rejected(new ContextDependentAsyncResultConsumer<Throwable>(suspendContext) {
207 public void consume(Throwable failure, @NotNull Vm vm) {
208 processBreakpointLogExpressionAndSuspend(breakpoint, xSuspendContext, suspendContext);
214 private void processBreakpointLogExpressionAndSuspend(@NotNull final XBreakpoint<?> breakpoint,
215 @NotNull final SuspendContextImpl xSuspendContext,
216 @NotNull SuspendContext suspendContext) {
217 XExpression logExpressionObject = breakpoint.getLogExpressionObject();
218 final String logExpression = logExpressionObject == null ? null : logExpressionObject.getExpression();
220 if (logExpression == null) {
221 breakpointReached(breakpoint, null, xSuspendContext);
224 xSuspendContext.evaluateExpression(logExpression).done(new ContextDependentAsyncResultConsumer<String>(suspendContext) {
226 public void consume(String logResult, @NotNull Vm vm) {
227 breakpointReached(breakpoint, logResult, xSuspendContext);
229 }).rejected(new ContextDependentAsyncResultConsumer<Throwable>(suspendContext) {
231 public void consume(Throwable logResult, @NotNull Vm vm) {
232 breakpointReached(breakpoint, "Failed to evaluate expression: " + logExpression, xSuspendContext);
238 private void breakpointReached(@NotNull XBreakpoint<?> breakpoint,
239 @Nullable String evaluatedLogExpression,
240 @NotNull XSuspendContext suspendContext) {
241 if (getSession().breakpointReached(breakpoint, evaluatedLogExpression, suspendContext)) {
250 public final void startPausing() {
251 connection.getVm().getSuspendContextManager().suspend().rejected(new RejectErrorReporter(getSession(), "Cannot pause"));
255 public final String getCurrentStateMessage() {
256 return connection.getState().getMessage();
261 public final HyperlinkListener getCurrentStateHyperlinkListener() {
262 return getConnection().getState().getMessageLinkListener();
266 protected final ProcessHandler doGetProcessHandler() {
267 return executionResult == null ? new SilentDestroyDebugProcessHandler() : executionResult.getProcessHandler();
270 private static final class SilentDestroyDebugProcessHandler extends DefaultDebugProcessHandler {
272 public boolean isSilentlyDestroyOnClose() {