IDEA-133347 Can't set breakpoint and debug in decompiled code
[idea/community.git] / platform / xdebugger-impl / src / com / intellij / xdebugger / impl / XDebuggerUtilImpl.java
1 /*
2  * Copyright 2000-2015 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.intellij.xdebugger.impl;
17
18 import com.intellij.lang.Language;
19 import com.intellij.openapi.actionSystem.CommonDataKeys;
20 import com.intellij.openapi.actionSystem.DataContext;
21 import com.intellij.openapi.application.Result;
22 import com.intellij.openapi.application.WriteAction;
23 import com.intellij.openapi.editor.Caret;
24 import com.intellij.openapi.editor.Document;
25 import com.intellij.openapi.editor.Editor;
26 import com.intellij.openapi.fileEditor.FileDocumentManager;
27 import com.intellij.openapi.fileEditor.FileEditorManager;
28 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
29 import com.intellij.openapi.fileTypes.StdFileTypes;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.util.text.StringUtil;
32 import com.intellij.openapi.vfs.VirtualFile;
33 import com.intellij.psi.*;
34 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
35 import com.intellij.psi.util.PsiTreeUtil;
36 import com.intellij.util.Processor;
37 import com.intellij.xdebugger.*;
38 import com.intellij.xdebugger.breakpoints.*;
39 import com.intellij.xdebugger.breakpoints.ui.XBreakpointGroupingRule;
40 import com.intellij.xdebugger.evaluation.EvaluationMode;
41 import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
42 import com.intellij.xdebugger.frame.XExecutionStack;
43 import com.intellij.xdebugger.frame.XStackFrame;
44 import com.intellij.xdebugger.frame.XSuspendContext;
45 import com.intellij.xdebugger.frame.XValueContainer;
46 import com.intellij.xdebugger.impl.breakpoints.XBreakpointUtil;
47 import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl;
48 import com.intellij.xdebugger.impl.breakpoints.ui.grouping.XBreakpointFileGroupingRule;
49 import com.intellij.xdebugger.impl.evaluate.quick.common.ValueLookupManager;
50 import com.intellij.xdebugger.impl.settings.XDebuggerSettingsManager;
51 import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase;
52 import com.intellij.xdebugger.settings.XDebuggerSettings;
53 import gnu.trove.THashMap;
54 import org.jetbrains.annotations.NotNull;
55 import org.jetbrains.annotations.Nullable;
56
57 import java.util.*;
58
59 /**
60  * @author nik
61  */
62 public class XDebuggerUtilImpl extends XDebuggerUtil {
63   private XLineBreakpointType<?>[] myLineBreakpointTypes;
64   private Map<Class<? extends XBreakpointType>, XBreakpointType<?,?>> myBreakpointTypeByClass;
65
66   @Override
67   public XLineBreakpointType<?>[] getLineBreakpointTypes() {
68     if (myLineBreakpointTypes == null) {
69       XBreakpointType[] types = XBreakpointUtil.getBreakpointTypes();
70       List<XLineBreakpointType<?>> lineBreakpointTypes = new ArrayList<XLineBreakpointType<?>>();
71       for (XBreakpointType type : types) {
72         if (type instanceof XLineBreakpointType<?>) {
73           lineBreakpointTypes.add((XLineBreakpointType<?>)type);
74         }
75       }
76       myLineBreakpointTypes = lineBreakpointTypes.toArray(new XLineBreakpointType<?>[lineBreakpointTypes.size()]);
77     }
78     return myLineBreakpointTypes;
79   }
80
81   @Override
82   public void toggleLineBreakpoint(@NotNull final Project project, @NotNull final VirtualFile file, final int line, boolean temporary) {
83     XLineBreakpointType<?> typeWinner = null;
84     for (XLineBreakpointType<?> type : getLineBreakpointTypes()) {
85       if (type.canPutAt(file, line, project) && (typeWinner == null || type.getPriority() > typeWinner.getPriority())) {
86         typeWinner = type;
87       }
88     }
89     if (typeWinner != null) {
90       toggleLineBreakpoint(project, typeWinner, file, line, temporary);
91     }
92   }
93
94   @Override
95   public boolean canPutBreakpointAt(@NotNull Project project, @NotNull VirtualFile file, int line) {
96     for (XLineBreakpointType<?> type : getLineBreakpointTypes()) {
97       if (type.canPutAt(file, line, project)) {
98         return true;
99       }
100     }
101     return false;
102   }
103
104   @Override
105   public <P extends XBreakpointProperties> void toggleLineBreakpoint(@NotNull final Project project,
106                                                                      @NotNull final XLineBreakpointType<P> type,
107                                                                      @NotNull final VirtualFile file,
108                                                                      final int line,
109                                                                      final boolean temporary) {
110     toggleAndReturnLineBreakpoint(project, type, file, line, temporary);
111   }
112
113   public static <P extends XBreakpointProperties> XLineBreakpoint toggleAndReturnLineBreakpoint(@NotNull final Project project,
114                                                                                                 @NotNull final XLineBreakpointType<P> type,
115                                                                                                 @NotNull final VirtualFile file,
116                                                                                                 final int line,
117                                                                                                 final boolean temporary) {
118     return new WriteAction<XLineBreakpoint>() {
119       @Override
120       protected void run(@NotNull final Result<XLineBreakpoint> result) {
121         XBreakpointManager breakpointManager = XDebuggerManager.getInstance(project).getBreakpointManager();
122         XLineBreakpoint<P> breakpoint = breakpointManager.findBreakpointAtLine(type, file, line);
123         if (breakpoint != null) {
124           breakpointManager.removeBreakpoint(breakpoint);
125         }
126         else {
127           P properties = type.createBreakpointProperties(file, line);
128           result.setResult(breakpointManager.addLineBreakpoint(type, file.getUrl(), line, properties, temporary));
129         }
130       }
131     }.execute().getResultObject();
132   }
133
134   @Override
135   public void removeBreakpoint(final Project project, final XBreakpoint<?> breakpoint) {
136     new WriteAction() {
137       @Override
138       protected void run(@NotNull final Result result) {
139         XDebuggerManager.getInstance(project).getBreakpointManager().removeBreakpoint(breakpoint);
140       }
141     }.execute();
142   }
143
144   @Override
145   public <B extends XBreakpoint<?>> XBreakpointType<B, ?> findBreakpointType(@NotNull Class<? extends XBreakpointType<B, ?>> typeClass) {
146     if (myBreakpointTypeByClass == null) {
147       myBreakpointTypeByClass = new THashMap<Class<? extends XBreakpointType>, XBreakpointType<?,?>>();
148       for (XBreakpointType<?, ?> breakpointType : XBreakpointUtil.getBreakpointTypes()) {
149         myBreakpointTypeByClass.put(breakpointType.getClass(), breakpointType);
150       }
151     }
152     XBreakpointType<?, ?> type = myBreakpointTypeByClass.get(typeClass);
153     //noinspection unchecked
154     return (XBreakpointType<B, ?>)type;
155   }
156
157   @Override
158   public <T extends XDebuggerSettings<?>> T getDebuggerSettings(Class<T> aClass) {
159     return XDebuggerSettingsManager.getInstanceImpl().getSettings(aClass);
160   }
161
162   @Override
163   public XValueContainer getValueContainer(DataContext dataContext) {
164     return XDebuggerTreeActionBase.getSelectedValue(dataContext);
165   }
166
167   @Override
168   @Nullable
169   public XSourcePosition createPosition(final VirtualFile file, final int line) {
170     return XSourcePositionImpl.create(file, line);
171   }
172
173   @Override
174   @Nullable
175   public XSourcePosition createPositionByOffset(final VirtualFile file, final int offset) {
176     return XSourcePositionImpl.createByOffset(file, offset);
177   }
178
179   @Override
180   public <B extends XLineBreakpoint<?>> XBreakpointGroupingRule<B, ?> getGroupingByFileRule() {
181     return new XBreakpointFileGroupingRule<B>();
182   }
183
184   @Nullable
185   public static XSourcePosition getCaretPosition(@NotNull Project project, DataContext context) {
186     Editor editor = getEditor(project, context);
187     if (editor == null) return null;
188
189     final Document document = editor.getDocument();
190     final int line = editor.getCaretModel().getLogicalPosition().line;
191     VirtualFile file = FileDocumentManager.getInstance().getFile(document);
192     return XSourcePositionImpl.create(file, line);
193   }
194
195   @NotNull
196   public static Collection<XSourcePosition> getAllCaretsPositions(@NotNull Project project, DataContext context) {
197     Editor editor = getEditor(project, context);
198     if (editor == null) {
199       return Collections.emptyList();
200     }
201
202     final Document document = editor.getDocument();
203     VirtualFile file = FileDocumentManager.getInstance().getFile(document);
204     Collection<XSourcePosition> res = new ArrayList<XSourcePosition>();
205     List<Caret> carets = editor.getCaretModel().getAllCarets();
206     for (Caret caret : carets) {
207       int line = caret.getLogicalPosition().line;
208       XSourcePositionImpl position = XSourcePositionImpl.create(file, line);
209       if (position != null) {
210         res.add(position);
211       }
212     }
213     return res;
214   }
215
216   @Nullable
217   private static Editor getEditor(@NotNull Project project, DataContext context) {
218     Editor editor = CommonDataKeys.EDITOR.getData(context);
219     if(editor == null) {
220       return FileEditorManager.getInstance(project).getSelectedTextEditor();
221     }
222     return editor;
223   }
224
225   @Override
226   public <B extends XBreakpoint<?>> Comparator<B> getDefaultBreakpointComparator(final XBreakpointType<B, ?> type) {
227     return new Comparator<B>() {
228       @Override
229       public int compare(final B o1, final B o2) {
230         return type.getDisplayText(o1).compareTo(type.getDisplayText(o2));
231       }
232     };
233   }
234
235   @Override
236   public <P extends XBreakpointProperties> Comparator<XLineBreakpoint<P>> getDefaultLineBreakpointComparator() {
237     return new Comparator<XLineBreakpoint<P>>() {
238       @Override
239       public int compare(final XLineBreakpoint<P> o1, final XLineBreakpoint<P> o2) {
240         int fileCompare = o1.getFileUrl().compareTo(o2.getFileUrl());
241         if (fileCompare != 0) return fileCompare;
242         return o1.getLine() - o2.getLine();
243       }
244     };
245   }
246
247   @Nullable
248   public static XDebuggerEvaluator getEvaluator(final XSuspendContext suspendContext) {
249     XExecutionStack executionStack = suspendContext.getActiveExecutionStack();
250     if (executionStack != null) {
251       XStackFrame stackFrame = executionStack.getTopFrame();
252       if (stackFrame != null) {
253         return stackFrame.getEvaluator();
254       }
255     }
256     return null;
257   }
258
259   @Override
260   public void iterateLine(@NotNull Project project, @NotNull Document document, int line, @NotNull Processor<PsiElement> processor) {
261     PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(document);
262     if (file == null) {
263       return;
264     }
265
266     int lineStart;
267     int lineEnd;
268     try {
269       lineStart = document.getLineStartOffset(line);
270       lineEnd = document.getLineEndOffset(line);
271     }
272     catch (IndexOutOfBoundsException ignored) {
273       return;
274     }
275
276     PsiElement element;
277     int offset = lineStart;
278
279     if (file instanceof PsiCompiledFile) {
280       file = ((PsiCompiledFile)file).getDecompiledPsiFile();
281     }
282
283     while (offset < lineEnd) {
284       element = file.findElementAt(offset);
285       if (element != null) {
286         if (!processor.process(element)) {
287           return;
288         }
289         else {
290           offset = element.getTextRange().getEndOffset();
291         }
292       }
293       else {
294         offset++;
295       }
296     }
297   }
298
299   @Override
300   public <B extends XLineBreakpoint<?>> List<XBreakpointGroupingRule<B, ?>> getGroupingByFileRuleAsList() {
301     return Collections.<XBreakpointGroupingRule<B, ?>>singletonList(this.<B>getGroupingByFileRule());
302   }
303
304   @Override
305   @Nullable
306   public PsiElement findContextElement(@NotNull VirtualFile virtualFile, int offset, @NotNull Project project, boolean checkXml) {
307     if (!virtualFile.isValid()) {
308       return null;
309     }
310
311     Document document = FileDocumentManager.getInstance().getDocument(virtualFile);
312     PsiFile file = document == null ? null : PsiManager.getInstance(project).findFile(virtualFile);
313     if (file == null) {
314       return null;
315     }
316
317     if (offset < 0) {
318       offset = 0;
319     }
320     if (offset > document.getTextLength()) {
321       offset = document.getTextLength();
322     }
323     int startOffset = offset;
324
325     int lineEndOffset = document.getLineEndOffset(document.getLineNumber(offset));
326     PsiElement result = null;
327     do {
328       PsiElement element = file.findElementAt(offset);
329       if (!(element instanceof PsiWhiteSpace) && !(element instanceof PsiComment)) {
330         result = element;
331         break;
332       }
333
334       offset = element.getTextRange().getEndOffset() + 1;
335     }
336     while (offset < lineEndOffset);
337
338     if (result == null) {
339       result = file.findElementAt(startOffset);
340     }
341
342     if (checkXml && result != null && StdFileTypes.XML.getLanguage().equals(result.getLanguage())) {
343       PsiLanguageInjectionHost parent = PsiTreeUtil.getParentOfType(result, PsiLanguageInjectionHost.class);
344       if (parent != null) {
345         result = InjectedLanguageUtil.findElementInInjected(parent, offset);
346       }
347     }
348     return result;
349   }
350
351   @Override
352   public void disableValueLookup(@NotNull Editor editor) {
353     ValueLookupManager.DISABLE_VALUE_LOOKUP.set(editor, Boolean.TRUE);
354   }
355
356   @Nullable
357   public static Editor createEditor(@NotNull OpenFileDescriptor descriptor) {
358     return descriptor.canNavigate() ? FileEditorManager.getInstance(descriptor.getProject()).openTextEditor(descriptor, false) : null;
359   }
360
361   public static void rebuildAllSessionsViews(@Nullable Project project) {
362     if (project == null) return;
363     for (XDebugSession session : XDebuggerManager.getInstance(project).getDebugSessions()) {
364       if (session.isSuspended()) {
365         session.rebuildViews();
366       }
367     }
368   }
369
370   @NotNull
371   @Override
372   public XExpression createExpression(@NotNull String text, Language language, String custom, EvaluationMode mode) {
373     return new XExpressionImpl(text, language, custom, mode);
374   }
375
376   public static boolean isEmptyExpression(@Nullable XExpression expression) {
377     return expression == null || StringUtil.isEmptyOrSpaces(expression.getExpression());
378   }
379 }