--Add support for resolving source positions inside inner level elements
[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.application.AccessToken;
28 import com.intellij.openapi.application.ApplicationInfo;
29 import com.intellij.openapi.application.ApplicationManager;
30 import com.intellij.openapi.diagnostic.Logger;
31 import com.intellij.openapi.editor.Document;
32 import com.intellij.openapi.extensions.Extensions;
33 import com.intellij.openapi.fileEditor.FileDocumentManager;
34 import com.intellij.openapi.module.Module;
35 import com.intellij.openapi.module.ModuleUtilCore;
36 import com.intellij.openapi.progress.ProgressIndicator;
37 import com.intellij.openapi.progress.ProgressManager;
38 import com.intellij.openapi.progress.Task;
39 import com.intellij.openapi.project.Project;
40 import com.intellij.openapi.ui.Messages;
41 import com.intellij.openapi.util.Key;
42 import com.intellij.openapi.util.Ref;
43 import com.intellij.openapi.vfs.VirtualFile;
44 import com.intellij.psi.PsiElement;
45 import com.intellij.psi.PsiFile;
46 import com.intellij.psi.PsiManager;
47 import com.intellij.psi.ResolveState;
48 import com.intellij.psi.scope.PsiScopeProcessor;
49 import com.intellij.psi.util.PsiTreeUtil;
50 import com.intellij.remote.RemoteProcessControl;
51 import com.intellij.util.ui.UIUtil;
52 import com.intellij.xdebugger.*;
53 import com.intellij.xdebugger.breakpoints.XBreakpoint;
54 import com.intellij.xdebugger.breakpoints.XBreakpointHandler;
55 import com.intellij.xdebugger.breakpoints.XBreakpointType;
56 import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
57 import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
58 import com.intellij.xdebugger.frame.XSuspendContext;
59 import com.intellij.xdebugger.frame.XValueChildrenList;
60 import com.intellij.xdebugger.impl.XSourcePositionImpl;
61 import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
62 import com.jetbrains.python.PythonFileType;
63 import com.jetbrains.python.console.PythonDebugLanguageConsoleView;
64 import com.jetbrains.python.console.pydev.PydevCompletionVariant;
65 import com.jetbrains.python.debugger.pydev.*;
66 import com.jetbrains.python.psi.*;
67 import com.jetbrains.python.psi.resolve.PyResolveContext;
68 import com.jetbrains.python.psi.resolve.PyResolveUtil;
69 import com.jetbrains.python.psi.resolve.RatedResolveResult;
70 import com.jetbrains.python.psi.types.PyClassType;
71 import com.jetbrains.python.psi.types.PyModuleType;
72 import com.jetbrains.python.psi.types.PyType;
73 import com.jetbrains.python.psi.types.PyTypeParser;
74 import org.jetbrains.annotations.NotNull;
75 import org.jetbrains.annotations.Nullable;
76
77 import java.io.IOException;
78 import java.net.ServerSocket;
79 import java.util.*;
80 import java.util.concurrent.ConcurrentHashMap;
81
82 import static javax.swing.SwingUtilities.invokeLater;
83
84 /**
85  * @author yole
86  */
87 // todo: bundle messages
88 // todo: pydevd supports module reloading - look for a way to use the feature
89 public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, ProcessListener {
90
91   private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.debugger.PyDebugProcess");
92   private static final int CONNECTION_TIMEOUT = 60000;
93
94   private final ProcessDebugger myDebugger;
95   private final XBreakpointHandler[] myBreakpointHandlers;
96   private final PyDebuggerEditorsProvider myEditorsProvider;
97   private final ProcessHandler myProcessHandler;
98   private final ExecutionConsole myExecutionConsole;
99   private final Map<PySourcePosition, XLineBreakpoint> myRegisteredBreakpoints = new ConcurrentHashMap<PySourcePosition, XLineBreakpoint>();
100   private final Map<String, XBreakpoint<? extends ExceptionBreakpointProperties>> myRegisteredExceptionBreakpoints =
101     new ConcurrentHashMap<String, XBreakpoint<? extends ExceptionBreakpointProperties>>();
102
103   private final List<PyThreadInfo> mySuspendedThreads = Collections.synchronizedList(Lists.<PyThreadInfo>newArrayList());
104   private final Map<String, XValueChildrenList> myStackFrameCache = Maps.newHashMap();
105   private final Map<String, PyDebugValue> myNewVariableValue = Maps.newHashMap();
106   private boolean myDownloadSources = false;
107
108   private boolean myClosing = false;
109
110   private PyPositionConverter myPositionConverter;
111   private final XSmartStepIntoHandler<?> mySmartStepIntoHandler;
112   private boolean myWaitingForConnection = false;
113   private PyStackFrame myConsoleContextFrame = null;
114   private PyReferrersLoader myReferrersProvider;
115
116   public PyDebugProcess(final @NotNull XDebugSession session,
117                         @NotNull final ServerSocket serverSocket,
118                         @NotNull final ExecutionConsole executionConsole,
119                         @Nullable final ProcessHandler processHandler, boolean multiProcess) {
120     super(session);
121     session.setPauseActionSupported(true);
122     if (multiProcess) {
123       myDebugger = createMultiprocessDebugger(serverSocket);
124     }
125     else {
126       myDebugger = new RemoteDebugger(this, serverSocket, getConnectTimeout());
127     }
128
129     List<XBreakpointHandler> breakpointHandlers = new ArrayList<XBreakpointHandler>();
130     breakpointHandlers.add(new PyLineBreakpointHandler(this));
131     breakpointHandlers.add(new PyExceptionBreakpointHandler(this));
132     for (PyBreakpointHandlerFactory factory : Extensions.getExtensions(PyBreakpointHandlerFactory.EP_NAME)) {
133       breakpointHandlers.add(factory.createBreakpointHandler(this));
134     }
135     myBreakpointHandlers = breakpointHandlers.toArray(new XBreakpointHandler[breakpointHandlers.size()]);
136
137     myEditorsProvider = new PyDebuggerEditorsProvider();
138     mySmartStepIntoHandler = new PySmartStepIntoHandler(this);
139     myProcessHandler = processHandler;
140     myExecutionConsole = executionConsole;
141     if (myProcessHandler != null) {
142       myProcessHandler.addProcessListener(this);
143     }
144     if (processHandler instanceof PositionConverterProvider) {
145       myPositionConverter = ((PositionConverterProvider)processHandler).createPositionConverter(this);
146     }
147     else {
148       myPositionConverter = new PyLocalPositionConverter();
149     }
150     myDebugger.addCloseListener(new RemoteDebuggerCloseListener() {
151       @Override
152       public void closed() {
153         handleStop();
154       }
155
156       @Override
157       public void communicationError() {
158         detachDebuggedProcess();
159       }
160
161       @Override
162       public void detached() {
163         detachDebuggedProcess();
164       }
165     });
166   }
167
168   private MultiProcessDebugger createMultiprocessDebugger(ServerSocket serverSocket) {
169     MultiProcessDebugger debugger = new MultiProcessDebugger(this, serverSocket, 10000);
170     debugger.addOtherDebuggerCloseListener(new MultiProcessDebugger.DebuggerProcessListener() {
171       @Override
172       public void threadsClosed(Set<String> threadIds) {
173         for (PyThreadInfo t : mySuspendedThreads) {
174           if (threadIds.contains(t.getId())) {
175             if (getSession().isSuspended()) {
176               getSession().resume();
177               break;
178             }
179           }
180         }
181       }
182     });
183     return debugger;
184   }
185
186   protected void detachDebuggedProcess() {
187     handleStop(); //in case of normal debug we stop the session
188   }
189
190   protected void handleStop() {
191     getSession().stop();
192   }
193
194   public void setPositionConverter(PyPositionConverter positionConverter) {
195     myPositionConverter = positionConverter;
196   }
197
198
199   @Override
200   public PyPositionConverter getPositionConverter() {
201     return myPositionConverter;
202   }
203
204   @NotNull
205   @Override
206   public XBreakpointHandler<?>[] getBreakpointHandlers() {
207     return myBreakpointHandlers;
208   }
209
210   @Override
211   @NotNull
212   public XDebuggerEditorsProvider getEditorsProvider() {
213     return myEditorsProvider;
214   }
215
216   @Override
217   @Nullable
218   protected ProcessHandler doGetProcessHandler() {
219     return myProcessHandler;
220   }
221
222   @Override
223   @NotNull
224   public ExecutionConsole createConsole() {
225     return myExecutionConsole;
226   }
227
228   @Override
229   public XSmartStepIntoHandler<?> getSmartStepIntoHandler() {
230     return mySmartStepIntoHandler;
231   }
232
233   @Override
234   public void sessionInitialized() {
235     waitForConnection(getConnectionMessage(), getConnectionTitle());
236   }
237
238   protected void waitForConnection(final String connectionMessage, String connectionTitle) {
239     ProgressManager.getInstance().run(new Task.Backgroundable(getSession().getProject(), connectionTitle, false) {
240       @Override
241       public void run(@NotNull final ProgressIndicator indicator) {
242         indicator.setText(connectionMessage);
243         try {
244           beforeConnect();
245           myWaitingForConnection = true;
246           myDebugger.waitForConnect();
247           myWaitingForConnection = false;
248           afterConnect();
249
250           handshake();
251           init();
252           myDebugger.run();
253         }
254         catch (final Exception e) {
255           myWaitingForConnection = false;
256           if (myProcessHandler != null) {
257             myProcessHandler.destroyProcess();
258           }
259           if (!myClosing) {
260             invokeLater(new Runnable() {
261               @Override
262               public void run() {
263                 Messages.showErrorDialog("Unable to establish connection with debugger:\n" + e.getMessage(), getConnectionTitle());
264               }
265             });
266           }
267         }
268       }
269     });
270   }
271
272   @Override
273   public void init() {
274     getSession().rebuildViews();
275     registerBreakpoints();
276   }
277
278   @Override
279   public int handleDebugPort(int localPort) throws IOException {
280     if (myProcessHandler instanceof RemoteProcessControl) {
281       return getRemoteTunneledPort(localPort, (RemoteProcessControl)myProcessHandler);
282     }
283     else {
284       return localPort;
285     }
286   }
287
288   protected static int getRemoteTunneledPort(int localPort, @NotNull RemoteProcessControl handler) throws IOException {
289     try {
290       return handler.getRemoteSocket(localPort).getSecond();
291     }
292     catch (Exception e) {
293       throw new IOException(e);
294     }
295   }
296
297   @Override
298   public void recordSignature(PySignature signature) {
299     PySignatureCacheManager.getInstance(getSession().getProject()).recordSignature(myPositionConverter.convertSignature(signature));
300   }
301
302   @Override
303   public void recordLogEvent(PyConcurrencyEvent event) {
304     PyConcurrencyService.getInstance(getSession().getProject()).recordEvent(getSession(), event, event.isAsyncio());
305   }
306
307   @Override
308   public void showConsole(PyThreadInfo thread) {
309     myConsoleContextFrame = new PyExecutionStack(this, thread).getTopFrame();
310     if (myExecutionConsole instanceof PythonDebugLanguageConsoleView) {
311       UIUtil.invokeLaterIfNeeded(new Runnable() {
312         @Override
313         public void run() {
314           ((PythonDebugLanguageConsoleView)myExecutionConsole).enableConsole(false);
315         }
316       });
317     }
318   }
319
320   protected void afterConnect() {
321   }
322
323   protected void beforeConnect() {
324   }
325
326   protected String getConnectionMessage() {
327     return "Waiting for connection...";
328   }
329
330   protected String getConnectionTitle() {
331     return "Connecting To Debugger";
332   }
333
334   private void handshake() throws PyDebuggerException {
335     String remoteVersion = myDebugger.handshake();
336     String currentBuild = ApplicationInfo.getInstance().getBuild().asStringWithoutProductCode();
337     if ("@@BUILD_NUMBER@@".equals(remoteVersion)) {
338       remoteVersion = currentBuild;
339     }
340     else if (remoteVersion.startsWith("PY-")) {
341       remoteVersion = remoteVersion.substring(3);
342     }
343     else {
344       remoteVersion = null;
345     }
346     printToConsole("Connected to pydev debugger (build " + remoteVersion + ")\n", ConsoleViewContentType.SYSTEM_OUTPUT);
347
348     if (remoteVersion != null) {
349       if (!(remoteVersion.equals(currentBuild) || remoteVersion.startsWith(currentBuild))) {
350         LOG.warn(String.format("Wrong debugger version. Remote version: %s Current build: %s", remoteVersion, currentBuild));
351         printToConsole("Warning: wrong debugger version. Use pycharm-debugger.egg from PyCharm installation folder.\n",
352                        ConsoleViewContentType.ERROR_OUTPUT);
353       }
354     }
355   }
356
357   @Override
358   public void printToConsole(String text, ConsoleViewContentType contentType) {
359     ((ConsoleView)myExecutionConsole).print(text, contentType);
360   }
361
362   private void registerBreakpoints() {
363     registerLineBreakpoints();
364     registerExceptionBreakpoints();
365   }
366
367   private void registerExceptionBreakpoints() {
368     for (XBreakpoint<? extends ExceptionBreakpointProperties> bp : myRegisteredExceptionBreakpoints.values()) {
369       addExceptionBreakpoint(bp);
370     }
371   }
372
373   public void registerLineBreakpoints() {
374     for (Map.Entry<PySourcePosition, XLineBreakpoint> entry : myRegisteredBreakpoints.entrySet()) {
375       addBreakpoint(entry.getKey(), entry.getValue());
376     }
377   }
378
379   @Override
380   public void startStepOver(@Nullable XSuspendContext context) {
381     passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_OVER);
382   }
383
384   @Override
385   public void startStepInto(@Nullable XSuspendContext context) {
386     passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_INTO);
387   }
388
389   public void startStepIntoMyCode(@Nullable XSuspendContext context) {
390     if (!checkCanPerformCommands()) return;
391     getSession().sessionResumed();
392     passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_INTO_MY_CODE);
393   }
394
395   @Override
396   public void startStepOut(@Nullable XSuspendContext context) {
397     passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_OUT);
398   }
399
400   public void startSmartStepInto(String functionName) {
401     dropFrameCaches();
402     if (isConnected()) {
403       for (PyThreadInfo suspendedThread : mySuspendedThreads) {
404         myDebugger.smartStepInto(suspendedThread.getId(), functionName);
405       }
406     }
407   }
408
409   @Override
410   public void stop() {
411     myDebugger.close();
412   }
413
414   @Override
415   public void resume(@Nullable XSuspendContext context) {
416     passToAllThreads(ResumeOrStepCommand.Mode.RESUME);
417   }
418
419   @Override
420   public void startPausing() {
421     if (isConnected()) {
422       myDebugger.suspendAllThreads();
423     }
424   }
425
426   private void passToAllThreads(final ResumeOrStepCommand.Mode mode) {
427     dropFrameCaches();
428     if (isConnected()) {
429       for (PyThreadInfo suspendedThread : Lists.newArrayList(mySuspendedThreads)) {
430         myDebugger.resumeOrStep(suspendedThread.getId(), mode);
431       }
432     }
433   }
434
435   private void passToCurrentThread(@Nullable XSuspendContext context, final ResumeOrStepCommand.Mode mode) {
436     dropFrameCaches();
437     if (isConnected()) {
438       String threadId = threadIdBeforeResumeOrStep(context);
439
440       for (PyThreadInfo suspendedThread : mySuspendedThreads) {
441         if (threadId == null || threadId.equals(suspendedThread.getId())) {
442           myDebugger.resumeOrStep(suspendedThread.getId(), mode);
443           break;
444         }
445       }
446     }
447   }
448
449   @Nullable
450   private static String threadIdBeforeResumeOrStep(@Nullable XSuspendContext context) {
451     if (context instanceof PySuspendContext) {
452       return ((PySuspendContext)context).getActiveExecutionStack().getThreadId();
453     }
454     else {
455       return null;
456     }
457   }
458
459   protected boolean isConnected() {
460     return myDebugger.isConnected();
461   }
462
463   protected void disconnect() {
464     myDebugger.disconnect();
465     cleanUp();
466   }
467
468   public boolean isDownloadSources() {
469     return myDownloadSources;
470   }
471
472   public void setDownloadSources(boolean downloadSources) {
473     myDownloadSources = downloadSources;
474   }
475
476   protected void cleanUp() {
477     mySuspendedThreads.clear();
478     myDownloadSources = false;
479   }
480
481   @Override
482   public void runToPosition(@NotNull final XSourcePosition position, @Nullable XSuspendContext context) {
483     dropFrameCaches();
484     if (isConnected() && !mySuspendedThreads.isEmpty()) {
485       final PySourcePosition pyPosition = myPositionConverter.convertToPython(position);
486       String type = PyLineBreakpointType.ID;
487       AccessToken lock = ApplicationManager.getApplication().acquireReadActionLock();
488       try {
489         final Document document = FileDocumentManager.getInstance().getDocument(position.getFile());
490         if (document != null) {
491           for (XBreakpointType breakpointType : Extensions.getExtensions(XBreakpointType.EXTENSION_POINT_NAME)) {
492             if (breakpointType instanceof PyBreakpointType &&
493                 ((PyBreakpointType)breakpointType).canPutInDocument(getSession().getProject(), document)) {
494               type = breakpointType.getId();
495               break;
496             }
497           }
498         }
499       }
500       finally {
501         lock.finish();
502       }
503       myDebugger.setTempBreakpoint(type, pyPosition.getFile(), pyPosition.getLine());
504
505       passToCurrentThread(context, ResumeOrStepCommand.Mode.RESUME);
506     }
507   }
508
509   @Override
510   public PyDebugValue evaluate(final String expression, final boolean execute, boolean doTrunc) throws PyDebuggerException {
511     dropFrameCaches();
512     final PyStackFrame frame = currentFrame();
513     return evaluate(expression, execute, frame, doTrunc);
514   }
515
516   private PyDebugValue evaluate(String expression, boolean execute, PyStackFrame frame, boolean trimResult) throws PyDebuggerException {
517     return myDebugger.evaluate(frame.getThreadId(), frame.getFrameId(), expression, execute, trimResult);
518   }
519
520   public void consoleExec(String command, PyDebugCallback<String> callback) {
521     dropFrameCaches();
522     try {
523       final PyStackFrame frame = currentFrame();
524       myDebugger.consoleExec(frame.getThreadId(), frame.getFrameId(), command, callback);
525     }
526     catch (PyDebuggerException e) {
527       callback.error(e);
528     }
529   }
530
531   @Override
532   @Nullable
533   public XValueChildrenList loadFrame() throws PyDebuggerException {
534     final PyStackFrame frame = currentFrame();
535     //do not reload frame every time it is needed, because due to bug in pdb, reloading frame clears all variable changes
536     if (!myStackFrameCache.containsKey(frame.getThreadFrameId())) {
537       XValueChildrenList values = myDebugger.loadFrame(frame.getThreadId(), frame.getFrameId());
538       myStackFrameCache.put(frame.getThreadFrameId(), values);
539     }
540     return applyNewValue(myStackFrameCache.get(frame.getThreadFrameId()), frame.getThreadFrameId());
541   }
542
543   private XValueChildrenList applyNewValue(XValueChildrenList pyDebugValues, String threadFrameId) {
544     if (myNewVariableValue.containsKey(threadFrameId)) {
545       PyDebugValue newValue = myNewVariableValue.get(threadFrameId);
546       XValueChildrenList res = new XValueChildrenList();
547       for (int i = 0; i < pyDebugValues.size(); i++) {
548         final String name = pyDebugValues.getName(i);
549         if (name.equals(newValue.getName())) {
550           res.add(name, newValue);
551         }
552         else {
553           res.add(name, pyDebugValues.getValue(i));
554         }
555       }
556       return res;
557     }
558     else {
559       return pyDebugValues;
560     }
561   }
562
563   @Override
564   public XValueChildrenList loadVariable(final PyDebugValue var) throws PyDebuggerException {
565     final PyStackFrame frame = currentFrame();
566     return myDebugger.loadVariable(frame.getThreadId(), frame.getFrameId(), var);
567   }
568
569   @Override
570   public void loadReferrers(PyReferringObjectsValue var, PyDebugCallback<XValueChildrenList> callback) {
571     try {
572       final PyStackFrame frame = currentFrame();
573       myDebugger.loadReferrers(frame.getThreadId(), frame.getFrameId(), var, callback);
574     }
575     catch (PyDebuggerException e) {
576       callback.error(e);
577     }
578   }
579
580   @Override
581   public void changeVariable(final PyDebugValue var, final String value) throws PyDebuggerException {
582     final PyStackFrame frame = currentFrame();
583     PyDebugValue newValue = myDebugger.changeVariable(frame.getThreadId(), frame.getFrameId(), var, value);
584     myNewVariableValue.put(frame.getThreadFrameId(), newValue);
585   }
586
587   @Nullable
588   @Override
589   public PyReferrersLoader getReferrersLoader() {
590     if (myReferrersProvider == null) {
591       myReferrersProvider = new PyReferrersLoader(this);
592     }
593     return myReferrersProvider;
594   }
595
596   @Override
597   public ArrayChunk getArrayItems(PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format)
598     throws PyDebuggerException {
599     final PyStackFrame frame = currentFrame();
600     return myDebugger.loadArrayItems(frame.getThreadId(), frame.getFrameId(), var, rowOffset, colOffset, rows, cols, format);
601   }
602
603   @Nullable
604   public String loadSource(String path) {
605     return myDebugger.loadSource(path);
606   }
607
608   @Override
609   public boolean canSaveToTemp(String name) {
610     final Project project = getSession().getProject();
611     return PyDebugSupportUtils.canSaveToTemp(project, name);
612   }
613
614   @NotNull
615   private PyStackFrame currentFrame() throws PyDebuggerException {
616     if (!isConnected()) {
617       throw new PyDebuggerException("Disconnected");
618     }
619
620     final PyStackFrame frame = (PyStackFrame)getSession().getCurrentStackFrame();
621
622     if (frame == null && myConsoleContextFrame != null) {
623       return myConsoleContextFrame;
624     }
625
626     if (frame == null) {
627       throw new PyDebuggerException("Process is running");
628     }
629
630     return frame;
631   }
632
633   @Nullable
634   private String getFunctionName(final XLineBreakpoint breakpoint) {
635     if (breakpoint.getSourcePosition() == null) {
636       return null;
637     }
638     final VirtualFile file = breakpoint.getSourcePosition().getFile();
639     AccessToken lock = ApplicationManager.getApplication().acquireReadActionLock();
640     try {
641       final Document document = FileDocumentManager.getInstance().getDocument(file);
642       final Project project = getSession().getProject();
643       if (document != null) {
644         if (file.getFileType() == PythonFileType.INSTANCE) {
645           PsiElement psiElement = XDebuggerUtil.getInstance().findContextElement(file, breakpoint.getSourcePosition().getOffset(),
646                                                                                  project, false);
647           PyFunction function = PsiTreeUtil.getParentOfType(psiElement, PyFunction.class);
648           if (function != null) {
649             return function.getName();
650           }
651         }
652       }
653       return null;
654     }
655     finally {
656       lock.finish();
657     }
658   }
659
660   public void addBreakpoint(final PySourcePosition position, final XLineBreakpoint breakpoint) {
661     myRegisteredBreakpoints.put(position, breakpoint);
662     if (isConnected()) {
663       final String conditionExpression = breakpoint.getConditionExpression() == null
664                                          ? null
665                                          : breakpoint.getConditionExpression().getExpression();
666       final String logExpression = breakpoint.getLogExpressionObject() == null
667                                    ? null
668                                    : breakpoint.getLogExpressionObject().getExpression();
669       myDebugger.setBreakpointWithFuncName(breakpoint.getType().getId(), position.getFile(), position.getLine(),
670                                            conditionExpression,
671                                            logExpression,
672                                            getFunctionName(breakpoint));
673     }
674   }
675
676   public void addTemporaryBreakpoint(String typeId, String file, int line) {
677     if (isConnected()) {
678       myDebugger.setTempBreakpoint(typeId, file, line);
679     }
680   }
681
682   public void removeBreakpoint(final PySourcePosition position) {
683     XLineBreakpoint breakpoint = myRegisteredBreakpoints.get(position);
684     if (breakpoint != null) {
685       myRegisteredBreakpoints.remove(position);
686       if (isConnected()) {
687         myDebugger.removeBreakpoint(breakpoint.getType().getId(), position.getFile(), position.getLine());
688       }
689     }
690   }
691
692   public void addExceptionBreakpoint(XBreakpoint<? extends ExceptionBreakpointProperties> breakpoint) {
693     myRegisteredExceptionBreakpoints.put(breakpoint.getProperties().getException(), breakpoint);
694     if (isConnected()) {
695       myDebugger.addExceptionBreakpoint(breakpoint.getProperties());
696     }
697   }
698
699   public void removeExceptionBreakpoint(XBreakpoint<? extends ExceptionBreakpointProperties> breakpoint) {
700     myRegisteredExceptionBreakpoints.remove(breakpoint.getProperties().getException());
701     if (isConnected()) {
702       myDebugger.removeExceptionBreakpoint(breakpoint.getProperties());
703     }
704   }
705
706   public Collection<PyThreadInfo> getThreads() {
707     return myDebugger.getThreads();
708   }
709
710   @Override
711   public void threadSuspended(final PyThreadInfo threadInfo) {
712     if (!mySuspendedThreads.contains(threadInfo)) {
713       mySuspendedThreads.add(threadInfo);
714
715       final List<PyStackFrameInfo> frames = threadInfo.getFrames();
716       if (frames != null) {
717         final PySuspendContext suspendContext = createSuspendContext(threadInfo);
718
719         XBreakpoint<?> breakpoint = null;
720         if (threadInfo.isStopOnBreakpoint()) {
721           final PySourcePosition position = frames.get(0).getPosition();
722           breakpoint = myRegisteredBreakpoints.get(position);
723           if (breakpoint == null) {
724             myDebugger.removeTempBreakpoint(position.getFile(), position.getLine());
725           }
726         }
727         else if (threadInfo.isExceptionBreak()) {
728           String exceptionName = threadInfo.getMessage();
729           threadInfo.setMessage(null);
730           if (exceptionName != null) {
731             breakpoint = myRegisteredExceptionBreakpoints.get(exceptionName);
732           }
733         }
734
735         if (breakpoint != null) {
736           if (!getSession().breakpointReached(breakpoint, threadInfo.getMessage(), suspendContext)) {
737             resume(suspendContext);
738           }
739         }
740         else {
741           getSession().positionReached(suspendContext);
742         }
743       }
744     }
745   }
746
747   @NotNull
748   protected PySuspendContext createSuspendContext(PyThreadInfo threadInfo) {
749     return new PySuspendContext(this, threadInfo);
750   }
751
752   @Override
753   public void threadResumed(final PyThreadInfo threadInfo) {
754     mySuspendedThreads.remove(threadInfo);
755   }
756
757   private void dropFrameCaches() {
758     myStackFrameCache.clear();
759     myNewVariableValue.clear();
760   }
761
762   @NotNull
763   public List<PydevCompletionVariant> getCompletions(String prefix) throws Exception {
764     if (isConnected()) {
765       dropFrameCaches();
766       final PyStackFrame frame = currentFrame();
767       return myDebugger.getCompletions(frame.getThreadId(), frame.getFrameId(), prefix);
768     }
769     return Lists.newArrayList();
770   }
771
772   @Override
773   public void startNotified(ProcessEvent event) {
774   }
775
776   @Override
777   public void processTerminated(ProcessEvent event) {
778     myDebugger.close();
779   }
780
781   @Override
782   public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) {
783     myClosing = true;
784   }
785
786   @Override
787   public void onTextAvailable(ProcessEvent event, Key outputType) {
788   }
789
790   public PyStackFrame createStackFrame(PyStackFrameInfo frameInfo) {
791     return new PyStackFrame(getSession().getProject(), this, frameInfo,
792                             getPositionConverter().convertFromPython(frameInfo.getPosition()));
793   }
794
795   @Override
796   public String getCurrentStateMessage() {
797     if (getSession().isStopped()) {
798       return XDebuggerBundle.message("debugger.state.message.disconnected");
799     }
800     else if (isConnected()) {
801       return XDebuggerBundle.message("debugger.state.message.connected");
802     }
803     else {
804       return getConnectionMessage();
805     }
806   }
807
808   public void addProcessListener(ProcessListener listener) {
809     ProcessHandler handler = doGetProcessHandler();
810     if (handler != null) {
811       handler.addProcessListener(listener);
812     }
813   }
814
815   public boolean isWaitingForConnection() {
816     return myWaitingForConnection;
817   }
818
819   public void setWaitingForConnection(boolean waitingForConnection) {
820     myWaitingForConnection = waitingForConnection;
821   }
822
823   public int getConnectTimeout() {
824     return CONNECTION_TIMEOUT;
825   }
826
827
828   @Nullable
829   private XSourcePosition getCurrentFrameSourcePosition() {
830     try {
831       PyStackFrame frame = currentFrame();
832
833       return frame.getSourcePosition();
834     }
835     catch (PyDebuggerException e) {
836       return null;
837     }
838   }
839
840   public Project getProject() {
841     return getSession().getProject();
842   }
843
844   @Nullable
845   @Override
846   public XSourcePosition getSourcePositionForName(String name, String parentType) {
847     if (name == null) return null;
848     XSourcePosition currentPosition = getCurrentFrameSourcePosition();
849
850     final PsiFile file = getPsiFile(currentPosition);
851
852     if (file == null) return null;
853
854     if (Strings.isNullOrEmpty(parentType)) {
855       final Ref<PsiElement> elementRef = resolveInCurrentFrame(name, currentPosition, file);
856       return elementRef.isNull() ? null : XSourcePositionImpl.createByElement(elementRef.get());
857     }
858     else {
859       final PyType parentDef = resolveTypeFromString(parentType, file);
860       if (parentDef == null)
861         return null;
862       List<? extends RatedResolveResult> results =
863         parentDef.resolveMember(name, null, AccessDirection.READ, PyResolveContext.noImplicits());
864       if (results != null && !results.isEmpty()) {
865         return XSourcePositionImpl.createByElement(results.get(0).getElement());
866       }
867       else {
868           return typeToPosition(parentDef); // at least try to return parent
869       }
870
871     }
872
873   }
874
875
876
877   @NotNull
878   private static Ref<PsiElement> resolveInCurrentFrame(final String name, XSourcePosition currentPosition, PsiFile file) {
879     final Ref<PsiElement> elementRef = Ref.create();
880     PsiElement currentElement = file.findElementAt(currentPosition.getOffset());
881
882     if (currentElement == null) {
883       return elementRef;
884     }
885
886
887     PyResolveUtil.scopeCrawlUp(new PsiScopeProcessor() {
888       @Override
889       public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
890         if ((element instanceof PyImportElement)) {
891           PyImportElement importElement = (PyImportElement)element;
892           if (name.equals(importElement.getVisibleName())) {
893             if (elementRef.isNull()) {
894               elementRef.set(element);
895             }
896             return false;
897
898           }
899           return true;
900         }
901         else {
902           if (elementRef.isNull()) {
903             elementRef.set(element);
904           }
905           return false;
906         }
907       }
908
909       @Nullable
910       @Override
911       public <T> T getHint(@NotNull Key<T> hintKey) {
912         return null;
913       }
914
915       @Override
916       public void handleEvent(@NotNull Event event, @Nullable Object associated) {
917
918       }
919     }, currentElement, name, null);
920     return elementRef;
921   }
922
923   @Nullable
924   private PsiFile getPsiFile(XSourcePosition currentPosition) {
925     if (currentPosition == null) {
926       return null;
927     }
928
929     return PsiManager.getInstance(getProject()).findFile(currentPosition.getFile());
930   }
931
932
933   @Nullable
934   @Override
935   public XSourcePosition getSourcePositionForType(String typeName) {
936     XSourcePosition currentPosition = getCurrentFrameSourcePosition();
937
938     final PsiFile file = getPsiFile(currentPosition);
939
940     if (file == null || typeName == null || !(file instanceof PyFile)) return null;
941
942
943     final PyType pyType = resolveTypeFromString(typeName, file);
944     return pyType == null? null : typeToPosition(pyType);
945
946   }
947
948   @Nullable
949   private static XSourcePosition typeToPosition(PyType pyType) {
950     final PyClassType classType = PyUtil.as(pyType, PyClassType.class);
951
952     if (classType != null) {
953       return XSourcePositionImpl.createByElement(classType.getPyClass());
954     }
955
956     final PyModuleType moduleType = PyUtil.as(pyType, PyModuleType.class);
957     if (moduleType != null) {
958       return XSourcePositionImpl.createByElement(moduleType.getModule());
959     }
960     return null;
961   }
962
963   private PyType resolveTypeFromString(String typeName, PsiFile file) {
964     typeName = typeName.replace("__builtin__.", "");
965     PyType pyType = null;
966     if (!typeName.contains(".")) {
967
968       pyType = PyTypeParser.getTypeByName(file, typeName);
969     }
970     if (pyType == null)
971     {
972       PyElementGenerator generator = PyElementGenerator.getInstance(getProject());
973       PyPsiFacade psiFacade = PyPsiFacade.getInstance(getProject());
974       PsiFile dummyFile = generator.createDummyFile( ((PyFile)file).getLanguageLevel() , "");
975       Module moduleForFile = ModuleUtilCore.findModuleForPsiElement(file);
976       dummyFile.putUserData(ModuleUtilCore.KEY_MODULE, moduleForFile);
977
978       pyType = psiFacade.parseTypeAnnotation(typeName, dummyFile);
979
980     }
981     return pyType;
982   }
983 }