constructor reference: don't ignore constructor parameters during method reference...
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / engine / JavaDebugProcess.java
1 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.debugger.engine;
3
4 import com.intellij.debugger.DebuggerBundle;
5 import com.intellij.debugger.actions.DebuggerActions;
6 import com.intellij.debugger.actions.JvmSmartStepIntoActionHandler;
7 import com.intellij.debugger.engine.evaluation.EvaluationContext;
8 import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
9 import com.intellij.debugger.impl.*;
10 import com.intellij.debugger.jdi.StackFrameProxyImpl;
11 import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
12 import com.intellij.debugger.memory.component.MemoryViewDebugProcessData;
13 import com.intellij.debugger.memory.ui.ClassesFilteredView;
14 import com.intellij.debugger.settings.DebuggerSettings;
15 import com.intellij.debugger.ui.AlternativeSourceNotificationProvider;
16 import com.intellij.debugger.ui.DebuggerContentInfo;
17 import com.intellij.debugger.ui.breakpoints.Breakpoint;
18 import com.intellij.debugger.ui.impl.ThreadsPanel;
19 import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl;
20 import com.intellij.debugger.ui.impl.watch.MessageDescriptor;
21 import com.intellij.debugger.ui.impl.watch.NodeManagerImpl;
22 import com.intellij.debugger.ui.overhead.OverheadView;
23 import com.intellij.debugger.ui.tree.NodeDescriptor;
24 import com.intellij.execution.process.ProcessHandler;
25 import com.intellij.execution.ui.ExecutionConsole;
26 import com.intellij.execution.ui.ExecutionConsoleEx;
27 import com.intellij.execution.ui.RunnerLayoutUi;
28 import com.intellij.execution.ui.layout.PlaceInGrid;
29 import com.intellij.icons.AllIcons;
30 import com.intellij.openapi.actionSystem.*;
31 import com.intellij.openapi.project.Project;
32 import com.intellij.openapi.util.Comparing;
33 import com.intellij.openapi.util.Pair;
34 import com.intellij.openapi.util.registry.Registry;
35 import com.intellij.openapi.vfs.VirtualFile;
36 import com.intellij.ui.EditorNotifications;
37 import com.intellij.ui.content.Content;
38 import com.intellij.ui.content.ContentManagerAdapter;
39 import com.intellij.ui.content.ContentManagerEvent;
40 import com.intellij.util.containers.ContainerUtil;
41 import com.intellij.xdebugger.*;
42 import com.intellij.xdebugger.breakpoints.XBreakpoint;
43 import com.intellij.xdebugger.breakpoints.XBreakpointHandler;
44 import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
45 import com.intellij.xdebugger.frame.XStackFrame;
46 import com.intellij.xdebugger.frame.XSuspendContext;
47 import com.intellij.xdebugger.frame.XValueMarkerProvider;
48 import com.intellij.xdebugger.impl.XDebugSessionImpl;
49 import com.intellij.xdebugger.impl.XDebuggerInlayUtil;
50 import com.intellij.xdebugger.impl.XDebuggerUtilImpl;
51 import com.intellij.xdebugger.memory.component.InstancesTracker;
52 import com.intellij.xdebugger.memory.component.MemoryViewManager;
53 import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
54 import com.intellij.xdebugger.ui.XDebugTabLayouter;
55 import com.sun.jdi.event.Event;
56 import com.sun.jdi.event.LocatableEvent;
57 import one.util.streamex.StreamEx;
58 import org.jetbrains.annotations.NotNull;
59 import org.jetbrains.annotations.Nullable;
60 import org.jetbrains.java.debugger.JavaDebuggerEditorsProvider;
61
62 /**
63  * @author egor
64  */
65 public class JavaDebugProcess extends XDebugProcess {
66   private final DebuggerSession myJavaSession;
67   private final JavaDebuggerEditorsProvider myEditorsProvider;
68   private final XBreakpointHandler<?>[] myBreakpointHandlers;
69   private final NodeManagerImpl myNodeManager;
70   private final JvmSmartStepIntoActionHandler mySmartStepIntoActionHandler;
71
72   private static final JavaBreakpointHandlerFactory[] ourDefaultBreakpointHandlerFactories = {
73     JavaBreakpointHandler.JavaLineBreakpointHandler::new,
74     JavaBreakpointHandler.JavaExceptionBreakpointHandler::new,
75     JavaBreakpointHandler.JavaFieldBreakpointHandler::new,
76     JavaBreakpointHandler.JavaMethodBreakpointHandler::new,
77     JavaBreakpointHandler.JavaWildcardBreakpointHandler::new
78   };
79
80   public static JavaDebugProcess create(@NotNull final XDebugSession session, @NotNull final DebuggerSession javaSession) {
81     JavaDebugProcess res = new JavaDebugProcess(session, javaSession);
82     javaSession.getProcess().setXDebugProcess(res);
83     return res;
84   }
85
86   protected JavaDebugProcess(@NotNull final XDebugSession session, @NotNull final DebuggerSession javaSession) {
87     super(session);
88     myJavaSession = javaSession;
89     myEditorsProvider = new JavaDebuggerEditorsProvider();
90     final DebugProcessImpl process = javaSession.getProcess();
91
92     myBreakpointHandlers = StreamEx.of(ourDefaultBreakpointHandlerFactories)
93       .append(JavaBreakpointHandlerFactory.EP_NAME.extensions())
94       .map(factory -> factory.createHandler(process))
95       .toArray(XBreakpointHandler[]::new);
96
97     myJavaSession.getContextManager().addListener(new DebuggerContextListener() {
98       @Override
99       public void changeEvent(@NotNull final DebuggerContextImpl newContext, DebuggerSession.Event event) {
100         if (event == DebuggerSession.Event.PAUSE
101             || event == DebuggerSession.Event.CONTEXT
102             || event == DebuggerSession.Event.REFRESH
103             || event == DebuggerSession.Event.REFRESH_WITH_STACK
104                && myJavaSession.isPaused()) {
105           final SuspendContextImpl newSuspendContext = newContext.getSuspendContext();
106           if (newSuspendContext != null &&
107               (shouldApplyContext(newContext) || event == DebuggerSession.Event.REFRESH_WITH_STACK)) {
108             process.getManagerThread().schedule(new SuspendContextCommandImpl(newSuspendContext) {
109               @Override
110               public void contextAction(@NotNull SuspendContextImpl suspendContext) {
111                 ThreadReferenceProxyImpl threadProxy = newContext.getThreadProxy();
112                 newSuspendContext.initExecutionStacks(threadProxy);
113
114                 Pair<Breakpoint, Event> item = ContainerUtil.getFirstItem(DebuggerUtilsEx.getEventDescriptors(newSuspendContext));
115                 if (item != null) {
116                   XBreakpoint xBreakpoint = item.getFirst().getXBreakpoint();
117                   Event second = item.getSecond();
118                   if (xBreakpoint != null && second instanceof LocatableEvent &&
119                       threadProxy != null && ((LocatableEvent)second).thread() == threadProxy.getThreadReference()) {
120                     ((XDebugSessionImpl)getSession()).breakpointReachedNoProcessing(xBreakpoint, newSuspendContext);
121                     unsetPausedIfNeeded(newContext);
122                     SourceCodeChecker.checkSource(newContext);
123                     return;
124                   }
125                 }
126                 getSession().positionReached(newSuspendContext);
127                 unsetPausedIfNeeded(newContext);
128                 SourceCodeChecker.checkSource(newContext);
129               }
130             });
131           }
132         }
133         else if (event == DebuggerSession.Event.ATTACHED) {
134           getSession().rebuildViews(); // to refresh variables views message
135         }
136       }
137     });
138
139     myNodeManager = new NodeManagerImpl(session.getProject(), null) {
140       @NotNull
141       @Override
142       public DebuggerTreeNodeImpl createNode(final NodeDescriptor descriptor, EvaluationContext evaluationContext) {
143         return new DebuggerTreeNodeImpl(null, descriptor);
144       }
145
146       @Override
147       public DebuggerTreeNodeImpl createMessageNode(MessageDescriptor descriptor) {
148         return new DebuggerTreeNodeImpl(null, descriptor);
149       }
150
151       @NotNull
152       @Override
153       public DebuggerTreeNodeImpl createMessageNode(String message) {
154         return new DebuggerTreeNodeImpl(null, new MessageDescriptor(message));
155       }
156     };
157     session.addSessionListener(new XDebugSessionListener() {
158       @Override
159       public void sessionPaused() {
160         saveNodeHistory();
161         showAlternativeNotification(session.getCurrentStackFrame());
162       }
163
164       @Override
165       public void stackFrameChanged() {
166         XStackFrame frame = session.getCurrentStackFrame();
167         if (frame instanceof JavaStackFrame) {
168           showAlternativeNotification(frame);
169           StackFrameProxyImpl frameProxy = ((JavaStackFrame)frame).getStackFrameProxy();
170           DebuggerContextUtil.setStackFrame(javaSession.getContextManager(), frameProxy);
171           saveNodeHistory(frameProxy);
172         }
173       }
174
175       private void showAlternativeNotification(@Nullable XStackFrame frame) {
176         if (frame != null) {
177           XSourcePosition position = frame.getSourcePosition();
178           if (position != null) {
179             VirtualFile file = position.getFile();
180             if (!AlternativeSourceNotificationProvider.isFileProcessed(file)) {
181               EditorNotifications.getInstance(session.getProject()).updateNotifications(file);
182             }
183           }
184         }
185       }
186     });
187     if (Registry.is("debugger.show.values.between.lines") && session instanceof XDebugSessionImpl) {
188       ((XDebugSessionImpl)session).getSessionData().putUserData(XDebuggerInlayUtil.HELPER_KEY, new JavaDebuggerInlayUtil.Helper());
189     }
190
191     mySmartStepIntoActionHandler = new JvmSmartStepIntoActionHandler(javaSession);
192   }
193
194   private void unsetPausedIfNeeded(DebuggerContextImpl context) {
195     SuspendContextImpl suspendContext = context.getSuspendContext();
196     if (suspendContext != null && !suspendContext.suspends(context.getThreadProxy())) {
197       ((XDebugSessionImpl)getSession()).unsetPaused();
198     }
199   }
200
201   private boolean shouldApplyContext(DebuggerContextImpl context) {
202     SuspendContextImpl suspendContext = context.getSuspendContext();
203     SuspendContextImpl currentContext = (SuspendContextImpl)getSession().getSuspendContext();
204     if (suspendContext != null && !suspendContext.equals(currentContext)) return true;
205     JavaExecutionStack currentExecutionStack = currentContext != null ? currentContext.getActiveExecutionStack() : null;
206     return currentExecutionStack == null || !Comparing.equal(context.getThreadProxy(), currentExecutionStack.getThreadProxy());
207   }
208
209   public void saveNodeHistory() {
210     saveNodeHistory(getDebuggerStateManager().getContext().getFrameProxy());
211   }
212
213   private void saveNodeHistory(final StackFrameProxyImpl frameProxy) {
214     myJavaSession.getProcess().getManagerThread().invoke(PrioritizedTask.Priority.NORMAL,
215                                                          () -> myNodeManager.setHistoryByContext(frameProxy));
216   }
217
218   private DebuggerStateManager getDebuggerStateManager() {
219     return myJavaSession.getContextManager();
220   }
221
222   public DebuggerSession getDebuggerSession() {
223     return myJavaSession;
224   }
225
226   @NotNull
227   @Override
228   public XDebuggerEditorsProvider getEditorsProvider() {
229     return myEditorsProvider;
230   }
231
232   @Override
233   public void startStepOver(@Nullable XSuspendContext context) {
234     myJavaSession.stepOver(false);
235   }
236
237   @Override
238   public void startStepInto(@Nullable XSuspendContext context) {
239     myJavaSession.stepInto(false, null);
240   }
241
242   @Override
243   public void startForceStepInto(@Nullable XSuspendContext context) {
244     myJavaSession.stepInto(true, null);
245   }
246
247   @Override
248   public void startStepOut(@Nullable XSuspendContext context) {
249     myJavaSession.stepOut();
250   }
251
252   @Override
253   public void stop() {
254     myJavaSession.dispose();
255     myNodeManager.dispose();
256   }
257
258   @Override
259   public void startPausing() {
260     myJavaSession.pause();
261   }
262
263   @Override
264   public void resume(@Nullable XSuspendContext context) {
265     myJavaSession.resume();
266   }
267
268   @Override
269   public void runToPosition(@NotNull XSourcePosition position, @Nullable XSuspendContext context) {
270     myJavaSession.runToCursor(position, false);
271   }
272
273   @NotNull
274   @Override
275   public XBreakpointHandler<?>[] getBreakpointHandlers() {
276     return myBreakpointHandlers;
277   }
278
279   @Override
280   public boolean checkCanInitBreakpoints() {
281     return false;
282   }
283
284   @Nullable
285   @Override
286   protected ProcessHandler doGetProcessHandler() {
287     return myJavaSession.getProcess().getProcessHandler();
288   }
289
290   @NotNull
291   @Override
292   public ExecutionConsole createConsole() {
293     ExecutionConsole console = myJavaSession.getProcess().getExecutionResult().getExecutionConsole();
294     if (console != null) return console;
295     return super.createConsole();
296   }
297
298   @NotNull
299   @Override
300   public XDebugTabLayouter createTabLayouter() {
301     return new XDebugTabLayouter() {
302       @Override
303       public void registerAdditionalContent(@NotNull RunnerLayoutUi ui) {
304         registerThreadsPanel(ui);
305         registerMemoryViewPanel(ui);
306         registerOverheadMonitor(ui);
307       }
308
309       @NotNull
310       @Override
311       public Content registerConsoleContent(@NotNull RunnerLayoutUi ui, @NotNull ExecutionConsole console) {
312         Content content = null;
313         if (console instanceof ExecutionConsoleEx) {
314           ((ExecutionConsoleEx)console).buildUi(ui);
315           content = ui.findContent(DebuggerContentInfo.CONSOLE_CONTENT);
316         }
317         if (content == null) {
318           content = super.registerConsoleContent(ui, console);
319         }
320         return content;
321       }
322
323       private void registerThreadsPanel(@NotNull RunnerLayoutUi ui) {
324         final ThreadsPanel panel = new ThreadsPanel(myJavaSession.getProject(), getDebuggerStateManager());
325         final Content threadsContent = ui.createContent(
326           DebuggerContentInfo.THREADS_CONTENT, panel, XDebuggerBundle.message("debugger.session.tab.threads.title"),
327           null, null);
328         threadsContent.setCloseable(false);
329         ui.addContent(threadsContent, 0, PlaceInGrid.left, true);
330         ui.addListener(new ContentManagerAdapter() {
331           @Override
332           public void selectionChanged(@NotNull ContentManagerEvent event) {
333             if (event.getContent() == threadsContent) {
334               if (threadsContent.isSelected()) {
335                 panel.setUpdateEnabled(true);
336                 if (panel.isRefreshNeeded()) {
337                   panel.rebuildIfVisible(DebuggerSession.Event.CONTEXT);
338                 }
339               }
340               else {
341                 panel.setUpdateEnabled(false);
342               }
343             }
344           }
345         }, threadsContent);
346       }
347
348       private void registerMemoryViewPanel(@NotNull RunnerLayoutUi ui) {
349         if (!Registry.is("debugger.enable.memory.view")) return;
350
351         final XDebugSession session = getSession();
352         final DebugProcessImpl process = myJavaSession.getProcess();
353         final InstancesTracker tracker = InstancesTracker.getInstance(myJavaSession.getProject());
354
355         final ClassesFilteredView classesFilteredView = new ClassesFilteredView(session, process, tracker);
356
357         final Content memoryViewContent =
358           ui.createContent(MemoryViewManager.MEMORY_VIEW_CONTENT, classesFilteredView, "Memory",
359                            null, null);
360
361         memoryViewContent.setCloseable(false);
362         memoryViewContent.setShouldDisposeContent(true);
363
364         final MemoryViewDebugProcessData data = new MemoryViewDebugProcessData();
365         process.putUserData(MemoryViewDebugProcessData.KEY, data);
366         session.addSessionListener(new XDebugSessionListener() {
367           @Override
368           public void sessionStopped() {
369             session.removeSessionListener(this);
370             data.getTrackedStacks().clear();
371           }
372         });
373
374         ui.addContent(memoryViewContent, 0, PlaceInGrid.right, true);
375         final DebuggerManagerThreadImpl managerThread = process.getManagerThread();
376         ui.addListener(new ContentManagerAdapter() {
377           @Override
378           public void selectionChanged(@NotNull ContentManagerEvent event) {
379             if (event.getContent() == memoryViewContent) {
380               classesFilteredView.setActive(memoryViewContent.isSelected(), managerThread);
381             }
382           }
383         }, memoryViewContent);
384       }
385
386       private void registerOverheadMonitor(@NotNull RunnerLayoutUi ui) {
387         if (!Registry.is("debugger.enable.overhead.monitor")) return;
388
389         DebugProcessImpl process = myJavaSession.getProcess();
390         OverheadView monitor = new OverheadView(process);
391         Content overheadContent = ui.createContent("OverheadMonitor", monitor, "Overhead", null, null);
392
393         monitor.setBouncer(() -> ui.setBouncing(overheadContent, true));
394
395         overheadContent.setCloseable(false);
396         overheadContent.setShouldDisposeContent(true);
397
398         ui.addContent(overheadContent, 0, PlaceInGrid.right, true);
399       }
400     };
401   }
402
403   @Override
404   public void registerAdditionalActions(@NotNull DefaultActionGroup leftToolbar,
405                                         @NotNull DefaultActionGroup topToolbar,
406                                         @NotNull DefaultActionGroup settings) {
407     Constraints beforeRunner = new Constraints(Anchor.BEFORE, "Runner.Layout");
408     leftToolbar.add(Separator.getInstance(), beforeRunner);
409     leftToolbar.add(ActionManager.getInstance().getAction(DebuggerActions.DUMP_THREADS), beforeRunner);
410     leftToolbar.add(Separator.getInstance(), beforeRunner);
411
412     Constraints beforeSort = new Constraints(Anchor.BEFORE, "XDebugger.ToggleSortValues");
413     settings.addAction(new WatchLastMethodReturnValueAction(), beforeSort);
414     settings.addAction(new AutoVarsSwitchAction(), beforeSort);
415   }
416
417   private static class AutoVarsSwitchAction extends ToggleAction {
418     private volatile boolean myAutoModeEnabled;
419
420     AutoVarsSwitchAction() {
421       super(DebuggerBundle.message("action.auto.variables.mode"), DebuggerBundle.message("action.auto.variables.mode.description"), null);
422       myAutoModeEnabled = DebuggerSettings.getInstance().AUTO_VARIABLES_MODE;
423     }
424
425     @Override
426     public boolean isSelected(@NotNull AnActionEvent e) {
427       return myAutoModeEnabled;
428     }
429
430     @Override
431     public void setSelected(@NotNull AnActionEvent e, boolean enabled) {
432       myAutoModeEnabled = enabled;
433       DebuggerSettings.getInstance().AUTO_VARIABLES_MODE = enabled;
434       XDebuggerUtilImpl.rebuildAllSessionsViews(e.getProject());
435     }
436   }
437
438   private static class WatchLastMethodReturnValueAction extends ToggleAction {
439     private final String myText;
440     private final String myTextUnavailable;
441
442     WatchLastMethodReturnValueAction() {
443       super("", DebuggerBundle.message("action.watch.method.return.value.description"), null);
444       myText = DebuggerBundle.message("action.watches.method.return.value.enable");
445       myTextUnavailable = DebuggerBundle.message("action.watches.method.return.value.unavailable.reason");
446     }
447
448     @Override
449     public void update(@NotNull final AnActionEvent e) {
450       super.update(e);
451       final Presentation presentation = e.getPresentation();
452       DebugProcessImpl process = getCurrentDebugProcess(e.getProject());
453       if (process == null || process.canGetMethodReturnValue()) {
454         presentation.setEnabled(true);
455         presentation.setText(myText);
456       }
457       else {
458         presentation.setEnabled(false);
459         presentation.setText(myTextUnavailable);
460       }
461     }
462
463     @Override
464     public boolean isSelected(@NotNull AnActionEvent e) {
465       return DebuggerSettings.getInstance().WATCH_RETURN_VALUES;
466     }
467
468     @Override
469     public void setSelected(@NotNull AnActionEvent e, boolean watch) {
470       DebuggerSettings.getInstance().WATCH_RETURN_VALUES = watch;
471       DebugProcessImpl process = getCurrentDebugProcess(e.getProject());
472       if (process != null) {
473         process.setWatchMethodReturnValuesEnabled(watch);
474       }
475     }
476   }
477
478   @Nullable
479   public static DebugProcessImpl getCurrentDebugProcess(@Nullable Project project) {
480     if (project != null) {
481       XDebugSession session = XDebuggerManager.getInstance(project).getCurrentSession();
482       if (session != null) {
483         XDebugProcess process = session.getDebugProcess();
484         if (process instanceof JavaDebugProcess) {
485           return ((JavaDebugProcess)process).getDebuggerSession().getProcess();
486         }
487       }
488     }
489     return null;
490   }
491
492   public NodeManagerImpl getNodeManager() {
493     return myNodeManager;
494   }
495
496   @Override
497   public String getCurrentStateMessage() {
498     String description = myJavaSession.getStateDescription();
499     return description != null ? description : super.getCurrentStateMessage();
500   }
501
502   @Nullable
503   @Override
504   public XValueMarkerProvider<?, ?> createValueMarkerProvider() {
505     return new JavaValueMarker();
506   }
507
508   @Override
509   public boolean isLibraryFrameFilterSupported() {
510     return true;
511   }
512
513   @Nullable
514   @Override
515   public XSmartStepIntoHandler<?> getSmartStepIntoHandler() {
516     return mySmartStepIntoActionHandler;
517   }
518 }