PY-21230 Open numeric container viewers from "View" hyperlink
[idea/community.git] / python / src / com / jetbrains / python / debugger / PyDebugProcess.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.jetbrains.python.debugger;
17
18 import com.google.common.base.Strings;
19 import com.google.common.collect.Lists;
20 import com.google.common.collect.Maps;
21 import com.intellij.execution.process.ProcessEvent;
22 import com.intellij.execution.process.ProcessHandler;
23 import com.intellij.execution.process.ProcessListener;
24 import com.intellij.execution.ui.ConsoleView;
25 import com.intellij.execution.ui.ConsoleViewContentType;
26 import com.intellij.execution.ui.ExecutionConsole;
27 import com.intellij.openapi.actionSystem.AnActionEvent;
28 import com.intellij.openapi.actionSystem.DefaultActionGroup;
29 import com.intellij.openapi.actionSystem.Presentation;
30 import com.intellij.openapi.actionSystem.ToggleAction;
31 import com.intellij.openapi.application.AccessToken;
32 import com.intellij.openapi.application.ApplicationInfo;
33 import com.intellij.openapi.application.ApplicationManager;
34 import com.intellij.openapi.diagnostic.Logger;
35 import com.intellij.openapi.editor.Document;
36 import com.intellij.openapi.extensions.Extensions;
37 import com.intellij.openapi.fileEditor.FileDocumentManager;
38 import com.intellij.openapi.module.Module;
39 import com.intellij.openapi.module.ModuleUtilCore;
40 import com.intellij.openapi.progress.ProgressIndicator;
41 import com.intellij.openapi.progress.ProgressManager;
42 import com.intellij.openapi.progress.Task;
43 import com.intellij.openapi.project.Project;
44 import com.intellij.openapi.ui.Messages;
45 import com.intellij.openapi.util.Key;
46 import com.intellij.openapi.util.Ref;
47 import com.intellij.openapi.vfs.VirtualFile;
48 import com.intellij.psi.PsiElement;
49 import com.intellij.psi.PsiFile;
50 import com.intellij.psi.PsiManager;
51 import com.intellij.psi.ResolveState;
52 import com.intellij.psi.scope.PsiScopeProcessor;
53 import com.intellij.psi.util.PsiTreeUtil;
54 import com.intellij.remote.RemoteProcessControl;
55 import com.intellij.util.ui.UIUtil;
56 import com.intellij.xdebugger.*;
57 import com.intellij.xdebugger.breakpoints.*;
58 import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
59 import com.intellij.xdebugger.frame.XExecutionStack;
60 import com.intellij.xdebugger.frame.XStackFrame;
61 import com.intellij.xdebugger.frame.XSuspendContext;
62 import com.intellij.xdebugger.frame.XValueChildrenList;
63 import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
64 import com.jetbrains.python.PythonFileType;
65 import com.jetbrains.python.console.PythonConsoleView;
66 import com.jetbrains.python.console.PythonDebugLanguageConsoleView;
67 import com.jetbrains.python.console.pydev.PydevCompletionVariant;
68 import com.jetbrains.python.debugger.containerview.PyViewNumericContainerAction;
69 import com.jetbrains.python.debugger.pydev.*;
70 import com.jetbrains.python.debugger.settings.PyDebuggerSettings;
71 import com.jetbrains.python.psi.*;
72 import com.jetbrains.python.psi.resolve.PyResolveContext;
73 import com.jetbrains.python.psi.resolve.PyResolveUtil;
74 import com.jetbrains.python.psi.resolve.RatedResolveResult;
75 import com.jetbrains.python.psi.types.PyClassType;
76 import com.jetbrains.python.psi.types.PyModuleType;
77 import com.jetbrains.python.psi.types.PyType;
78 import com.jetbrains.python.psi.types.PyTypeParser;
79 import org.jetbrains.annotations.NotNull;
80 import org.jetbrains.annotations.Nullable;
81
82 import java.io.IOException;
83 import java.net.ServerSocket;
84 import java.util.*;
85 import java.util.concurrent.ConcurrentHashMap;
86
87 import static javax.swing.SwingUtilities.invokeLater;
88
89 /**
90  * @author yole
91  */
92 // todo: bundle messages
93 // todo: pydevd supports module reloading - look for a way to use the feature
94 public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, ProcessListener {
95
96   private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.debugger.PyDebugProcess");
97   private static final int CONNECTION_TIMEOUT = 60000;
98
99   private final ProcessDebugger myDebugger;
100   private final XBreakpointHandler[] myBreakpointHandlers;
101   private final PyDebuggerEditorsProvider myEditorsProvider;
102   private final ProcessHandler myProcessHandler;
103   private final ExecutionConsole myExecutionConsole;
104   private final Map<PySourcePosition, XLineBreakpoint> myRegisteredBreakpoints = new ConcurrentHashMap<>();
105   private final Map<String, XBreakpoint<? extends ExceptionBreakpointProperties>> myRegisteredExceptionBreakpoints =
106     new ConcurrentHashMap<>();
107
108   private final List<PyThreadInfo> mySuspendedThreads = Collections.synchronizedList(Lists.<PyThreadInfo>newArrayList());
109   private final Map<String, XValueChildrenList> myStackFrameCache = Maps.newHashMap();
110   private final Map<String, PyDebugValue> myNewVariableValue = Maps.newHashMap();
111   private boolean myDownloadSources = false;
112
113   private boolean myClosing = false;
114
115   private PyPositionConverter myPositionConverter;
116   private final XSmartStepIntoHandler<?> mySmartStepIntoHandler;
117   private boolean myWaitingForConnection = false;
118   private PyStackFrame myConsoleContextFrame = null;
119   private PyReferrersLoader myReferrersProvider;
120
121   public PyDebugProcess(@NotNull XDebugSession session,
122                         @NotNull ServerSocket serverSocket,
123                         @NotNull ExecutionConsole executionConsole,
124                         @Nullable ProcessHandler processHandler, boolean multiProcess) {
125     this(session, multiProcess ? process -> process.createMultiprocessDebugger(serverSocket)
126                                : process -> new RemoteDebugger(process, serverSocket, process.getConnectTimeout()),
127          executionConsole, processHandler);
128   }
129
130   public PyDebugProcess(final @NotNull XDebugSession session,
131                         @NotNull final ExecutionConsole executionConsole,
132                         @Nullable final ProcessHandler processHandler,
133                         @NotNull String serverHost, int serverPort) {
134     this(session, process -> new ClientModeMultiProcessDebugger(process, serverHost, serverPort), executionConsole, processHandler);
135   }
136
137   private PyDebugProcess(@NotNull XDebugSession session,
138                         @NotNull DebuggerFactory debuggerFactory,
139                         @NotNull ExecutionConsole executionConsole,
140                         @Nullable ProcessHandler processHandler) {
141     super(session);
142
143     session.setPauseActionSupported(true);
144
145     myDebugger = debuggerFactory.createDebugger(this);
146
147     List<XBreakpointHandler> breakpointHandlers = new ArrayList<>();
148     breakpointHandlers.add(new PyLineBreakpointHandler(this));
149     breakpointHandlers.add(new PyExceptionBreakpointHandler(this));
150     for (PyBreakpointHandlerFactory factory : Extensions.getExtensions(PyBreakpointHandlerFactory.EP_NAME)) {
151       breakpointHandlers.add(factory.createBreakpointHandler(this));
152     }
153     myBreakpointHandlers = breakpointHandlers.toArray(new XBreakpointHandler[breakpointHandlers.size()]);
154
155     myEditorsProvider = new PyDebuggerEditorsProvider();
156     mySmartStepIntoHandler = new PySmartStepIntoHandler(this);
157     myProcessHandler = processHandler;
158     myExecutionConsole = executionConsole;
159     if (myProcessHandler != null) {
160       myProcessHandler.addProcessListener(this);
161     }
162     if (processHandler instanceof PositionConverterProvider) {
163       myPositionConverter = ((PositionConverterProvider)processHandler).createPositionConverter(this);
164     }
165     else {
166       myPositionConverter = new PyLocalPositionConverter();
167     }
168     myDebugger.addCloseListener(new RemoteDebuggerCloseListener() {
169       @Override
170       public void closed() {
171         handleStop();
172       }
173
174       @Override
175       public void communicationError() {
176         detachDebuggedProcess();
177       }
178
179       @Override
180       public void detached() {
181         detachDebuggedProcess();
182       }
183     });
184
185     session.addSessionListener(new XDebugSessionListener() {
186       @Override
187       public void stackFrameChanged() {
188         String currentFrameThreadId = null;
189         final XStackFrame currentFrame = session.getCurrentStackFrame();
190         if (currentFrame instanceof PyStackFrame) {
191           currentFrameThreadId = ((PyStackFrame)currentFrame).getThreadId();
192         }
193         final XExecutionStack activeStack = session.getSuspendContext().getActiveExecutionStack();
194         if ((activeStack == null) || (currentFrameThreadId == null)) {
195           return;
196         }
197         final XStackFrame frameFromSuspendContext = activeStack.getTopFrame();
198         String activeStackThreadId = null;
199         if (frameFromSuspendContext instanceof PyStackFrame) {
200           activeStackThreadId = ((PyStackFrame)frameFromSuspendContext).getThreadId();
201         }
202         if (!currentFrameThreadId.equals(activeStackThreadId)) {
203           // another thread was selected, we should update suspendContext
204           PyThreadInfo threadInfo = null;
205           for (PyThreadInfo info : mySuspendedThreads) {
206             if (info.getId().equals(currentFrameThreadId)) {
207               threadInfo = info;
208               break;
209             }
210           }
211           if (threadInfo != null) {
212             getSession().positionReached(createSuspendContext(threadInfo));
213           }
214         }
215       }
216     });
217   }
218
219   private MultiProcessDebugger createMultiprocessDebugger(ServerSocket serverSocket) {
220     MultiProcessDebugger debugger = new MultiProcessDebugger(this, serverSocket, 10000);
221     debugger.addOtherDebuggerCloseListener(new MultiProcessDebugger.DebuggerProcessListener() {
222       @Override
223       public void threadsClosed(Set<String> threadIds) {
224         for (PyThreadInfo t : mySuspendedThreads) {
225           if (threadIds.contains(t.getId())) {
226             if (getSession().isSuspended()) {
227               getSession().resume();
228               break;
229             }
230           }
231         }
232       }
233     });
234     return debugger;
235   }
236
237   protected void detachDebuggedProcess() {
238     handleStop(); //in case of normal debug we stop the session
239   }
240
241   protected void handleStop() {
242     getSession().stop();
243   }
244
245   public void setPositionConverter(PyPositionConverter positionConverter) {
246     myPositionConverter = positionConverter;
247   }
248
249
250   @Override
251   public PyPositionConverter getPositionConverter() {
252     return myPositionConverter;
253   }
254
255   @NotNull
256   @Override
257   public XBreakpointHandler<?>[] getBreakpointHandlers() {
258     return myBreakpointHandlers;
259   }
260
261   @Override
262   @NotNull
263   public XDebuggerEditorsProvider getEditorsProvider() {
264     return myEditorsProvider;
265   }
266
267   @Override
268   @Nullable
269   protected ProcessHandler doGetProcessHandler() {
270     return myProcessHandler;
271   }
272
273   @Override
274   @NotNull
275   public ExecutionConsole createConsole() {
276     return myExecutionConsole;
277   }
278
279   @Override
280   public XSmartStepIntoHandler<?> getSmartStepIntoHandler() {
281     return mySmartStepIntoHandler;
282   }
283
284   @Override
285   public void sessionInitialized() {
286     waitForConnection(getConnectionMessage(), getConnectionTitle());
287   }
288
289   protected void waitForConnection(final String connectionMessage, String connectionTitle) {
290     ProgressManager.getInstance().run(new Task.Backgroundable(getSession().getProject(), connectionTitle, false) {
291       @Override
292       public void run(@NotNull final ProgressIndicator indicator) {
293         indicator.setText(connectionMessage);
294         try {
295           beforeConnect();
296           myWaitingForConnection = true;
297           myDebugger.waitForConnect();
298           myWaitingForConnection = false;
299           afterConnect();
300
301           handshake();
302           init();
303           myDebugger.run();
304         }
305         catch (final Exception e) {
306           myWaitingForConnection = false;
307           if (myProcessHandler != null) {
308             myProcessHandler.destroyProcess();
309           }
310           if (!myClosing) {
311             invokeLater(
312               () -> Messages.showErrorDialog("Unable to establish connection with debugger:\n" + e.getMessage(), getConnectionTitle()));
313           }
314         }
315       }
316     });
317   }
318
319   @Override
320   public void init() {
321     getSession().rebuildViews();
322     registerBreakpoints();
323     setShowReturnValues(PyDebuggerSettings.getInstance().isWatchReturnValues());
324   }
325
326   @Override
327   public int handleDebugPort(int localPort) throws IOException {
328     if (myProcessHandler instanceof RemoteProcessControl) {
329       return getRemoteTunneledPort(localPort, (RemoteProcessControl)myProcessHandler);
330     }
331     else {
332       return localPort;
333     }
334   }
335
336   protected static int getRemoteTunneledPort(int localPort, @NotNull RemoteProcessControl handler) throws IOException {
337     try {
338       return handler.getRemoteSocket(localPort).getSecond();
339     }
340     catch (Exception e) {
341       throw new IOException(e);
342     }
343   }
344
345   @Override
346   public void recordSignature(PySignature signature) {
347     PySignatureCacheManager.getInstance(getSession().getProject()).recordSignature(myPositionConverter.convertSignature(signature));
348   }
349
350   @Override
351   public void recordLogEvent(PyConcurrencyEvent event) {
352     PyConcurrencyService.getInstance(getSession().getProject()).recordEvent(getSession(), event, event.isAsyncio());
353   }
354
355   @Override
356   public void showConsole(PyThreadInfo thread) {
357     myConsoleContextFrame = new PyExecutionStack(this, thread).getTopFrame();
358     if (myExecutionConsole instanceof PythonDebugLanguageConsoleView) {
359       PythonDebugLanguageConsoleView consoleView = (PythonDebugLanguageConsoleView)myExecutionConsole;
360       UIUtil.invokeLaterIfNeeded(() -> {
361         consoleView.enableConsole(false);
362         consoleView.getPydevConsoleView().setConsoleEnabled(true);
363       });
364     }
365   }
366
367   @Override
368   public void consoleInputRequested(boolean isStarted) {
369     if (myExecutionConsole instanceof PythonDebugLanguageConsoleView) {
370       PythonConsoleView consoleView = ((PythonDebugLanguageConsoleView)myExecutionConsole).getPydevConsoleView();
371       if (isStarted) {
372         consoleView.inputRequested();
373       }
374       else {
375         consoleView.inputReceived();
376       }
377     }
378   }
379
380   protected void afterConnect() {
381   }
382
383   protected void beforeConnect() {
384   }
385
386   protected String getConnectionMessage() {
387     return "Waiting for connection...";
388   }
389
390   protected String getConnectionTitle() {
391     return "Connecting To Debugger";
392   }
393
394   private void handshake() throws PyDebuggerException {
395     String remoteVersion = myDebugger.handshake();
396     String currentBuild = ApplicationInfo.getInstance().getBuild().asStringWithoutProductCode();
397     if ("@@BUILD_NUMBER@@".equals(remoteVersion)) {
398       remoteVersion = currentBuild;
399     }
400     else if (remoteVersion.startsWith("PY-")) {
401       remoteVersion = remoteVersion.substring(3);
402     }
403     else {
404       remoteVersion = null;
405     }
406     printToConsole("Connected to pydev debugger (build " + remoteVersion + ")\n", ConsoleViewContentType.SYSTEM_OUTPUT);
407
408     if (remoteVersion != null) {
409       if (!(remoteVersion.equals(currentBuild) || remoteVersion.startsWith(currentBuild))) {
410         LOG.warn(String.format("Wrong debugger version. Remote version: %s Current build: %s", remoteVersion, currentBuild));
411         printToConsole("Warning: wrong debugger version. Use pycharm-debugger.egg from PyCharm installation folder.\n",
412                        ConsoleViewContentType.ERROR_OUTPUT);
413       }
414     }
415   }
416
417   @Override
418   public void printToConsole(String text, ConsoleViewContentType contentType) {
419     ((ConsoleView)myExecutionConsole).print(text, contentType);
420   }
421
422   private void registerBreakpoints() {
423     registerLineBreakpoints();
424     registerExceptionBreakpoints();
425   }
426
427   private void registerExceptionBreakpoints() {
428     for (XBreakpoint<? extends ExceptionBreakpointProperties> bp : myRegisteredExceptionBreakpoints.values()) {
429       addExceptionBreakpoint(bp);
430     }
431   }
432
433   public void registerLineBreakpoints() {
434     for (Map.Entry<PySourcePosition, XLineBreakpoint> entry : myRegisteredBreakpoints.entrySet()) {
435       addBreakpoint(entry.getKey(), entry.getValue());
436     }
437   }
438
439   @Override
440   public void registerAdditionalActions(@NotNull DefaultActionGroup leftToolbar,
441                                         @NotNull DefaultActionGroup topToolbar,
442                                         @NotNull DefaultActionGroup settings) {
443     super.registerAdditionalActions(leftToolbar, topToolbar, settings);
444     settings.add(new WatchReturnValuesAction(this));
445     settings.add(new SimplifiedView(this));
446   }
447
448   private static class WatchReturnValuesAction extends ToggleAction {
449     private volatile boolean myWatchesReturnValues;
450     private final PyDebugProcess myProcess;
451     private final String myText;
452
453     public WatchReturnValuesAction(@NotNull PyDebugProcess debugProcess) {
454       super("", "Enables watching executed functions return values", null);
455       myWatchesReturnValues = PyDebuggerSettings.getInstance().isWatchReturnValues();
456       myProcess = debugProcess;
457       myText = "Show Return Values";
458     }
459
460     @Override
461     public void update(@NotNull final AnActionEvent e) {
462       super.update(e);
463       final Presentation presentation = e.getPresentation();
464       presentation.setEnabled(true);
465       presentation.setText(myText);
466     }
467
468     @Override
469     public boolean isSelected(AnActionEvent e) {
470       return myWatchesReturnValues;
471     }
472
473     @Override
474     public void setSelected(AnActionEvent e, boolean watch) {
475       myWatchesReturnValues = watch;
476       PyDebuggerSettings.getInstance().setWatchReturnValues(watch);
477       final Project project = e.getProject();
478       if (project != null) {
479         myProcess.setShowReturnValues(myWatchesReturnValues);
480         myProcess.getSession().rebuildViews();
481       }
482     }
483   }
484
485   private static class SimplifiedView extends ToggleAction {
486     private volatile boolean mySimplifiedView;
487     private final PyDebugProcess myProcess;
488     private final String myText;
489
490     public SimplifiedView(@NotNull PyDebugProcess debugProcess) {
491       super("", "Disables watching classes, functions and modules objects", null);
492       mySimplifiedView = PyDebuggerSettings.getInstance().isSimplifiedView();
493       myProcess = debugProcess;
494       myText = "Simplified Variables View";
495     }
496
497     @Override
498     public void update(@NotNull final AnActionEvent e) {
499       super.update(e);
500       final Presentation presentation = e.getPresentation();
501       presentation.setEnabled(true);
502       presentation.setText(myText);
503     }
504
505     @Override
506     public boolean isSelected(AnActionEvent e) {
507       return mySimplifiedView;
508     }
509
510     @Override
511     public void setSelected(AnActionEvent e, boolean hide) {
512       mySimplifiedView = hide;
513       PyDebuggerSettings.getInstance().setSimplifiedView(hide);
514       myProcess.getSession().rebuildViews();
515     }
516   }
517
518   public void setShowReturnValues(boolean showReturnValues) {
519     myDebugger.setShowReturnValues(showReturnValues);
520   }
521
522   @Override
523   public void startStepOver(@Nullable XSuspendContext context) {
524     passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_OVER);
525   }
526
527   @Override
528   public void startStepInto(@Nullable XSuspendContext context) {
529     passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_INTO);
530   }
531
532   public void startStepIntoMyCode(@Nullable XSuspendContext context) {
533     if (!checkCanPerformCommands()) return;
534     getSession().sessionResumed();
535     passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_INTO_MY_CODE);
536   }
537
538   @Override
539   public void startStepOut(@Nullable XSuspendContext context) {
540     passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_OUT);
541   }
542
543   public void startSmartStepInto(String functionName) {
544     dropFrameCaches();
545     if (isConnected()) {
546       for (PyThreadInfo suspendedThread : mySuspendedThreads) {
547         myDebugger.smartStepInto(suspendedThread.getId(), functionName);
548       }
549     }
550   }
551
552   @Override
553   public void stop() {
554     myDebugger.close();
555   }
556
557   @Override
558   public void resume(@Nullable XSuspendContext context) {
559     passToAllThreads(ResumeOrStepCommand.Mode.RESUME);
560   }
561
562   @Override
563   public void startPausing() {
564     if (isConnected()) {
565       myDebugger.suspendAllThreads();
566     }
567   }
568
569   public void suspendAllOtherThreads(PyThreadInfo thread) {
570     myDebugger.suspendOtherThreads(thread);
571   }
572
573   /**
574    * Check if there is the thread suspended on the breakpoint with "Suspend all" policy
575    *
576    * @return true if this thread exists
577    */
578   @Override
579   public boolean isSuspendedOnAllThreadsPolicy() {
580     if (getSession().isSuspended()) {
581       for (PyThreadInfo threadInfo : getThreads()) {
582         final List<PyStackFrameInfo> frames = threadInfo.getFrames();
583         if ((threadInfo.getState() == PyThreadInfo.State.SUSPENDED) && (frames != null)) {
584           XBreakpoint<?> breakpoint = null;
585           if (threadInfo.isStopOnBreakpoint()) {
586             final PySourcePosition position = frames.get(0).getPosition();
587             breakpoint = myRegisteredBreakpoints.get(position);
588           }
589           else if (threadInfo.isExceptionBreak()) {
590             String exceptionName = threadInfo.getMessage();
591             if (exceptionName != null) {
592               breakpoint = myRegisteredExceptionBreakpoints.get(exceptionName);
593             }
594           }
595           if ((breakpoint != null) && (breakpoint.getType().isSuspendThreadSupported()) &&
596               (breakpoint.getSuspendPolicy() == SuspendPolicy.ALL)) {
597             return true;
598           }
599         }
600       }
601     }
602     return false;
603   }
604
605   private void passToAllThreads(final ResumeOrStepCommand.Mode mode) {
606     dropFrameCaches();
607     if (isConnected()) {
608       for (PyThreadInfo thread : myDebugger.getThreads()) {
609         myDebugger.resumeOrStep(thread.getId(), mode);
610       }
611     }
612   }
613
614   private void passToCurrentThread(@Nullable XSuspendContext context, final ResumeOrStepCommand.Mode mode) {
615     dropFrameCaches();
616     if (isConnected()) {
617       String threadId = threadIdBeforeResumeOrStep(context);
618
619       for (PyThreadInfo suspendedThread : mySuspendedThreads) {
620         if (threadId == null || threadId.equals(suspendedThread.getId())) {
621           myDebugger.resumeOrStep(suspendedThread.getId(), mode);
622           break;
623         }
624       }
625     }
626   }
627
628   @Nullable
629   private static String threadIdBeforeResumeOrStep(@Nullable XSuspendContext context) {
630     if (context instanceof PySuspendContext) {
631       return ((PySuspendContext)context).getActiveExecutionStack().getThreadId();
632     }
633     else {
634       return null;
635     }
636   }
637
638   protected boolean isConnected() {
639     return myDebugger.isConnected();
640   }
641
642   protected void disconnect() {
643     myDebugger.disconnect();
644     cleanUp();
645   }
646
647   public boolean isDownloadSources() {
648     return myDownloadSources;
649   }
650
651   public void setDownloadSources(boolean downloadSources) {
652     myDownloadSources = downloadSources;
653   }
654
655   protected void cleanUp() {
656     mySuspendedThreads.clear();
657     myDownloadSources = false;
658   }
659
660   @Override
661   public void runToPosition(@NotNull final XSourcePosition position, @Nullable XSuspendContext context) {
662     dropFrameCaches();
663     if (isConnected() && !mySuspendedThreads.isEmpty()) {
664       final PySourcePosition pyPosition = myPositionConverter.convertToPython(position);
665       String type = PyLineBreakpointType.ID;
666       AccessToken lock = ApplicationManager.getApplication().acquireReadActionLock();
667       try {
668         final Document document = FileDocumentManager.getInstance().getDocument(position.getFile());
669         if (document != null) {
670           for (XBreakpointType breakpointType : Extensions.getExtensions(XBreakpointType.EXTENSION_POINT_NAME)) {
671             if (breakpointType instanceof PyBreakpointType &&
672                 ((PyBreakpointType)breakpointType).canPutInDocument(getSession().getProject(), document)) {
673               type = breakpointType.getId();
674               break;
675             }
676           }
677         }
678       }
679       finally {
680         lock.finish();
681       }
682       myDebugger.setTempBreakpoint(type, pyPosition.getFile(), pyPosition.getLine());
683
684       passToCurrentThread(context, ResumeOrStepCommand.Mode.RESUME);
685     }
686   }
687
688   @Override
689   public PyDebugValue evaluate(final String expression, final boolean execute, boolean doTrunc) throws PyDebuggerException {
690     dropFrameCaches();
691     final PyStackFrame frame = currentFrame();
692     return evaluate(expression, execute, frame, doTrunc);
693   }
694
695   private PyDebugValue evaluate(String expression, boolean execute, PyStackFrame frame, boolean trimResult) throws PyDebuggerException {
696     return myDebugger.evaluate(frame.getThreadId(), frame.getFrameId(), expression, execute, trimResult);
697   }
698
699   public void consoleExec(String command, PyDebugCallback<String> callback) {
700     dropFrameCaches();
701     try {
702       final PyStackFrame frame = currentFrame();
703       myDebugger.consoleExec(frame.getThreadId(), frame.getFrameId(), command, callback);
704     }
705     catch (PyDebuggerException e) {
706       callback.error(e);
707     }
708   }
709
710   @Override
711   @Nullable
712   public XValueChildrenList loadFrame() throws PyDebuggerException {
713     final PyStackFrame frame = currentFrame();
714     //do not reload frame every time it is needed, because due to bug in pdb, reloading frame clears all variable changes
715     if (!myStackFrameCache.containsKey(frame.getThreadFrameId())) {
716       XValueChildrenList values = myDebugger.loadFrame(frame.getThreadId(), frame.getFrameId());
717       myStackFrameCache.put(frame.getThreadFrameId(), values);
718     }
719     return applyNewValue(myStackFrameCache.get(frame.getThreadFrameId()), frame.getThreadFrameId());
720   }
721
722   private XValueChildrenList applyNewValue(XValueChildrenList pyDebugValues, String threadFrameId) {
723     if (myNewVariableValue.containsKey(threadFrameId)) {
724       PyDebugValue newValue = myNewVariableValue.get(threadFrameId);
725       XValueChildrenList res = new XValueChildrenList();
726       for (int i = 0; i < pyDebugValues.size(); i++) {
727         final String name = pyDebugValues.getName(i);
728         if (name.equals(newValue.getName())) {
729           res.add(name, newValue);
730         }
731         else {
732           res.add(name, pyDebugValues.getValue(i));
733         }
734       }
735       return res;
736     }
737     else {
738       return pyDebugValues;
739     }
740   }
741
742   @Override
743   public XValueChildrenList loadVariable(final PyDebugValue var) throws PyDebuggerException {
744     final PyStackFrame frame = currentFrame();
745     PyDebugValue debugValue = var.setName(var.getFullName());
746     return myDebugger.loadVariable(frame.getThreadId(), frame.getFrameId(), debugValue);
747   }
748
749   @Override
750   public void loadReferrers(PyReferringObjectsValue var, PyDebugCallback<XValueChildrenList> callback) {
751     try {
752       final PyStackFrame frame = currentFrame();
753       myDebugger.loadReferrers(frame.getThreadId(), frame.getFrameId(), var, callback);
754     }
755     catch (PyDebuggerException e) {
756       callback.error(e);
757     }
758   }
759
760   @Override
761   public void changeVariable(final PyDebugValue var, final String value) throws PyDebuggerException {
762     final PyStackFrame frame = currentFrame();
763     PyDebugValue newValue = myDebugger.changeVariable(frame.getThreadId(), frame.getFrameId(), var, value);
764     myNewVariableValue.put(frame.getThreadFrameId(), newValue);
765   }
766
767   @Nullable
768   @Override
769   public PyReferrersLoader getReferrersLoader() {
770     if (myReferrersProvider == null) {
771       myReferrersProvider = new PyReferrersLoader(this);
772     }
773     return myReferrersProvider;
774   }
775
776   @Override
777   public ArrayChunk getArrayItems(PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format)
778     throws PyDebuggerException {
779     final PyStackFrame frame = currentFrame();
780     return myDebugger.loadArrayItems(frame.getThreadId(), frame.getFrameId(), var, rowOffset, colOffset, rows, cols, format);
781   }
782
783   @Nullable
784   public String loadSource(String path) {
785     return myDebugger.loadSource(path);
786   }
787
788   @Override
789   public boolean canSaveToTemp(String name) {
790     final Project project = getSession().getProject();
791     return PyDebugSupportUtils.canSaveToTemp(project, name);
792   }
793
794   @NotNull
795   private PyStackFrame currentFrame() throws PyDebuggerException {
796     if (!isConnected()) {
797       throw new PyDebuggerException("Disconnected");
798     }
799
800     final PyStackFrame frame = (PyStackFrame)getSession().getCurrentStackFrame();
801
802     if (frame == null && myConsoleContextFrame != null) {
803       return myConsoleContextFrame;
804     }
805
806     if (frame == null) {
807       throw new PyDebuggerException("Process is running");
808     }
809
810     return frame;
811   }
812
813   @Nullable
814   private String getFunctionName(final XLineBreakpoint breakpoint) {
815     if (breakpoint.getSourcePosition() == null) {
816       return null;
817     }
818     final VirtualFile file = breakpoint.getSourcePosition().getFile();
819     AccessToken lock = ApplicationManager.getApplication().acquireReadActionLock();
820     try {
821       final Document document = FileDocumentManager.getInstance().getDocument(file);
822       final Project project = getSession().getProject();
823       if (document != null) {
824         if (file.getFileType() == PythonFileType.INSTANCE) {
825           PsiElement psiElement = XDebuggerUtil.getInstance().
826             findContextElement(file, document.getLineStartOffset(breakpoint.getSourcePosition().getLine()), project, false);
827           PyFunction function = PsiTreeUtil.getParentOfType(psiElement, PyFunction.class);
828           if (function != null) {
829             return function.getName();
830           }
831         }
832       }
833       return null;
834     }
835     finally {
836       lock.finish();
837     }
838   }
839
840   public void addBreakpoint(final PySourcePosition position, final XLineBreakpoint breakpoint) {
841     myRegisteredBreakpoints.put(position, breakpoint);
842     if (isConnected()) {
843       final String conditionExpression = breakpoint.getConditionExpression() == null
844                                          ? null
845                                          : breakpoint.getConditionExpression().getExpression();
846       final String logExpression = breakpoint.getLogExpressionObject() == null
847                                    ? null
848                                    : breakpoint.getLogExpressionObject().getExpression();
849       SuspendPolicy policy = breakpoint.getType().isSuspendThreadSupported()? breakpoint.getSuspendPolicy(): SuspendPolicy.NONE;
850       myDebugger.setBreakpoint(breakpoint.getType().getId(),
851                                position.getFile(),
852                                position.getLine(),
853                                conditionExpression,
854                                logExpression,
855                                getFunctionName(breakpoint),
856                                policy
857       );
858     }
859   }
860
861   public void addTemporaryBreakpoint(String typeId, String file, int line) {
862     if (isConnected()) {
863       myDebugger.setTempBreakpoint(typeId, file, line);
864     }
865   }
866
867   public void removeBreakpoint(final PySourcePosition position) {
868     XLineBreakpoint breakpoint = myRegisteredBreakpoints.get(position);
869     if (breakpoint != null) {
870       myRegisteredBreakpoints.remove(position);
871       if (isConnected()) {
872         myDebugger.removeBreakpoint(breakpoint.getType().getId(), position.getFile(), position.getLine());
873       }
874     }
875   }
876
877   public void addExceptionBreakpoint(XBreakpoint<? extends ExceptionBreakpointProperties> breakpoint) {
878     myRegisteredExceptionBreakpoints.put(breakpoint.getProperties().getException(), breakpoint);
879     if (isConnected()) {
880       myDebugger.addExceptionBreakpoint(breakpoint.getProperties());
881     }
882   }
883
884   public void removeExceptionBreakpoint(XBreakpoint<? extends ExceptionBreakpointProperties> breakpoint) {
885     myRegisteredExceptionBreakpoints.remove(breakpoint.getProperties().getException());
886     if (isConnected()) {
887       myDebugger.removeExceptionBreakpoint(breakpoint.getProperties());
888     }
889   }
890
891   public Collection<PyThreadInfo> getThreads() {
892     return myDebugger.getThreads();
893   }
894
895   @Override
896   public void threadSuspended(final PyThreadInfo threadInfo, boolean updateSourcePosition) {
897     if (!mySuspendedThreads.contains(threadInfo)) {
898       mySuspendedThreads.add(threadInfo);
899
900       final List<PyStackFrameInfo> frames = threadInfo.getFrames();
901       if (frames != null) {
902         final PySuspendContext suspendContext = createSuspendContext(threadInfo);
903
904         XBreakpoint<?> breakpoint = null;
905         if (threadInfo.isStopOnBreakpoint()) {
906           final PySourcePosition position = frames.get(0).getPosition();
907           breakpoint = myRegisteredBreakpoints.get(position);
908           if (breakpoint == null) {
909             myDebugger.removeTempBreakpoint(position.getFile(), position.getLine());
910           }
911         }
912         else if (threadInfo.isExceptionBreak()) {
913           String exceptionName = threadInfo.getMessage();
914           threadInfo.setMessage(null);
915           if (exceptionName != null) {
916             breakpoint = myRegisteredExceptionBreakpoints.get(exceptionName);
917           }
918         }
919         if (breakpoint != null) {
920           if ((breakpoint.getType().isSuspendThreadSupported()) && (breakpoint.getSuspendPolicy() == SuspendPolicy.ALL)) {
921             suspendAllOtherThreads(threadInfo);
922           }
923         }
924
925         if (updateSourcePosition) {
926           if (breakpoint != null) {
927             if (!getSession().breakpointReached(breakpoint, threadInfo.getMessage(), suspendContext)) {
928               resume(suspendContext);
929             }
930           }
931           else {
932             getSession().positionReached(suspendContext);
933           }
934         }
935       }
936     }
937   }
938
939   @NotNull
940   protected PySuspendContext createSuspendContext(PyThreadInfo threadInfo) {
941     return new PySuspendContext(this, threadInfo);
942   }
943
944   @Override
945   public void threadResumed(final PyThreadInfo threadInfo) {
946     mySuspendedThreads.remove(threadInfo);
947   }
948
949   private void dropFrameCaches() {
950     myStackFrameCache.clear();
951     myNewVariableValue.clear();
952   }
953
954   @NotNull
955   public List<PydevCompletionVariant> getCompletions(String prefix) throws Exception {
956     if (isConnected()) {
957       dropFrameCaches();
958       final PyStackFrame frame = currentFrame();
959       return myDebugger.getCompletions(frame.getThreadId(), frame.getFrameId(), prefix);
960     }
961     return Lists.newArrayList();
962   }
963
964   @NotNull
965   public String getDescription(String prefix) throws Exception {
966     if (isConnected()) {
967       dropFrameCaches();
968       final PyStackFrame frame = currentFrame();
969       return myDebugger.getDescription(frame.getThreadId(), frame.getFrameId(), prefix);
970     }
971     return "";
972   }
973
974
975   @Override
976   public void startNotified(ProcessEvent event) {
977   }
978
979   @Override
980   public void processTerminated(ProcessEvent event) {
981     myDebugger.close();
982   }
983
984   @Override
985   public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) {
986     myClosing = true;
987   }
988
989   @Override
990   public void onTextAvailable(ProcessEvent event, Key outputType) {
991   }
992
993   public PyStackFrame createStackFrame(PyStackFrameInfo frameInfo) {
994     return new PyStackFrame(getSession().getProject(), this, frameInfo,
995                             getPositionConverter().convertFromPython(frameInfo.getPosition()));
996   }
997
998   @Override
999   public String getCurrentStateMessage() {
1000     if (getSession().isStopped()) {
1001       return XDebuggerBundle.message("debugger.state.message.disconnected");
1002     }
1003     else if (isConnected()) {
1004       return XDebuggerBundle.message("debugger.state.message.connected");
1005     }
1006     else {
1007       return getConnectionMessage();
1008     }
1009   }
1010
1011   public void addProcessListener(ProcessListener listener) {
1012     ProcessHandler handler = doGetProcessHandler();
1013     if (handler != null) {
1014       handler.addProcessListener(listener);
1015     }
1016   }
1017
1018   public boolean isWaitingForConnection() {
1019     return myWaitingForConnection;
1020   }
1021
1022   public void setWaitingForConnection(boolean waitingForConnection) {
1023     myWaitingForConnection = waitingForConnection;
1024   }
1025
1026   public int getConnectTimeout() {
1027     return CONNECTION_TIMEOUT;
1028   }
1029
1030
1031   @Nullable
1032   private XSourcePosition getCurrentFrameSourcePosition() {
1033     try {
1034       PyStackFrame frame = currentFrame();
1035
1036       return frame.getSourcePosition();
1037     }
1038     catch (PyDebuggerException e) {
1039       return null;
1040     }
1041   }
1042
1043   public Project getProject() {
1044     return getSession().getProject();
1045   }
1046
1047   @Nullable
1048   @Override
1049   public XSourcePosition getSourcePositionForName(String name, String parentType) {
1050     if (name == null) return null;
1051     XSourcePosition currentPosition = getCurrentFrameSourcePosition();
1052
1053     final PsiFile file = getPsiFile(currentPosition);
1054
1055     if (file == null) return null;
1056
1057     if (Strings.isNullOrEmpty(parentType)) {
1058       final Ref<PsiElement> elementRef = resolveInCurrentFrame(name, currentPosition, file);
1059       return elementRef.isNull() ? null : XDebuggerUtil.getInstance().createPositionByElement(elementRef.get());
1060     }
1061     else {
1062       final PyType parentDef = resolveTypeFromString(parentType, file);
1063       if (parentDef == null) {
1064         return null;
1065       }
1066       List<? extends RatedResolveResult> results =
1067         parentDef.resolveMember(name, null, AccessDirection.READ, PyResolveContext.noImplicits());
1068       if (results != null && !results.isEmpty()) {
1069         return XDebuggerUtil.getInstance().createPositionByElement(results.get(0).getElement());
1070       }
1071       else {
1072         return typeToPosition(parentDef); // at least try to return parent
1073       }
1074     }
1075   }
1076
1077
1078   @NotNull
1079   private static Ref<PsiElement> resolveInCurrentFrame(final String name, XSourcePosition currentPosition, PsiFile file) {
1080     final Ref<PsiElement> elementRef = Ref.create();
1081     PsiElement currentElement = file.findElementAt(currentPosition.getOffset());
1082
1083     if (currentElement == null) {
1084       return elementRef;
1085     }
1086
1087
1088     PyResolveUtil.scopeCrawlUp(new PsiScopeProcessor() {
1089       @Override
1090       public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
1091         if ((element instanceof PyImportElement)) {
1092           PyImportElement importElement = (PyImportElement)element;
1093           if (name.equals(importElement.getVisibleName())) {
1094             if (elementRef.isNull()) {
1095               elementRef.set(element);
1096             }
1097             return false;
1098           }
1099           return true;
1100         }
1101         else {
1102           if (elementRef.isNull()) {
1103             elementRef.set(element);
1104           }
1105           return false;
1106         }
1107       }
1108
1109       @Nullable
1110       @Override
1111       public <T> T getHint(@NotNull Key<T> hintKey) {
1112         return null;
1113       }
1114
1115       @Override
1116       public void handleEvent(@NotNull Event event, @Nullable Object associated) {
1117
1118       }
1119     }, currentElement, name, null);
1120     return elementRef;
1121   }
1122
1123   @Nullable
1124   private PsiFile getPsiFile(XSourcePosition currentPosition) {
1125     if (currentPosition == null) {
1126       return null;
1127     }
1128
1129     return PsiManager.getInstance(getProject()).findFile(currentPosition.getFile());
1130   }
1131
1132
1133   @Nullable
1134   @Override
1135   public XSourcePosition getSourcePositionForType(String typeName) {
1136     XSourcePosition currentPosition = getCurrentFrameSourcePosition();
1137
1138     final PsiFile file = getPsiFile(currentPosition);
1139
1140     if (file == null || typeName == null || !(file instanceof PyFile)) return null;
1141
1142
1143     final PyType pyType = resolveTypeFromString(typeName, file);
1144     return pyType == null ? null : typeToPosition(pyType);
1145   }
1146
1147   @Override
1148   public void showNumericContainer(PyDebugValue value) {
1149     PyViewNumericContainerAction.showNumericViewer(getProject(), value);
1150   }
1151
1152   @Nullable
1153   private static XSourcePosition typeToPosition(PyType pyType) {
1154     final PyClassType classType = PyUtil.as(pyType, PyClassType.class);
1155
1156     if (classType != null) {
1157       return XDebuggerUtil.getInstance().createPositionByElement(classType.getPyClass());
1158     }
1159
1160     final PyModuleType moduleType = PyUtil.as(pyType, PyModuleType.class);
1161     if (moduleType != null) {
1162       return XDebuggerUtil.getInstance().createPositionByElement(moduleType.getModule());
1163     }
1164     return null;
1165   }
1166
1167   private PyType resolveTypeFromString(String typeName, PsiFile file) {
1168     typeName = typeName.replace("__builtin__.", "");
1169     PyType pyType = null;
1170     if (!typeName.contains(".")) {
1171
1172       pyType = PyTypeParser.getTypeByName(file, typeName);
1173     }
1174     if (pyType == null) {
1175       PyElementGenerator generator = PyElementGenerator.getInstance(getProject());
1176       PyPsiFacade psiFacade = PyPsiFacade.getInstance(getProject());
1177       PsiFile dummyFile = generator.createDummyFile(((PyFile)file).getLanguageLevel(), "");
1178       Module moduleForFile = ModuleUtilCore.findModuleForPsiElement(file);
1179       dummyFile.putUserData(ModuleUtilCore.KEY_MODULE, moduleForFile);
1180
1181       pyType = psiFacade.parseTypeAnnotation(typeName, dummyFile);
1182     }
1183     return pyType;
1184   }
1185
1186   private interface DebuggerFactory {
1187     @NotNull ProcessDebugger createDebugger(@NotNull PyDebugProcess process);
1188   }
1189 }