fbf01ff46d5ecccbc037ee469437b563a3c41531
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / engine / RequestHint.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
17 /*
18  * @author: Eugene Zhuravlev
19  * Date: Jul 23, 2002
20  * Time: 11:10:11 AM
21  */
22 package com.intellij.debugger.engine;
23
24 import com.intellij.debugger.SourcePosition;
25 import com.intellij.debugger.engine.evaluation.EvaluateException;
26 import com.intellij.debugger.engine.jdi.StackFrameProxy;
27 import com.intellij.debugger.impl.DebuggerUtilsEx;
28 import com.intellij.debugger.impl.PositionUtil;
29 import com.intellij.debugger.jdi.StackFrameProxyImpl;
30 import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
31 import com.intellij.debugger.settings.DebuggerSettings;
32 import com.intellij.openapi.application.ApplicationManager;
33 import com.intellij.openapi.diagnostic.Logger;
34 import com.intellij.openapi.util.Computable;
35 import com.intellij.psi.PsiElement;
36 import com.intellij.util.Range;
37 import com.sun.jdi.Location;
38 import com.sun.jdi.Method;
39 import com.sun.jdi.VMDisconnectedException;
40 import com.sun.jdi.request.StepRequest;
41 import org.intellij.lang.annotations.MagicConstant;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
44
45 public class RequestHint {
46   public static final int STOP = 0;
47   public static final int RESUME = -100;
48
49   private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.RequestHint");
50   @MagicConstant (intValues = {StepRequest.STEP_MIN, StepRequest.STEP_LINE})
51   private final int mySize;
52   @MagicConstant (intValues = {StepRequest.STEP_INTO, StepRequest.STEP_OVER, StepRequest.STEP_OUT})
53   private final int myDepth;
54   private final SourcePosition myPosition;
55   private final int myFrameCount;
56   private boolean mySteppedOut = false;
57
58   @Nullable
59   private final MethodFilter myMethodFilter;
60   private boolean myTargetMethodMatched = false;
61
62   private boolean myIgnoreFilters = false;
63   private boolean myResetIgnoreFilters = false;
64   private boolean myRestoreBreakpoints = false;
65
66   public RequestHint(final ThreadReferenceProxyImpl stepThread, final SuspendContextImpl suspendContext, @NotNull MethodFilter methodFilter) {
67     this(stepThread, suspendContext, StepRequest.STEP_LINE, StepRequest.STEP_INTO, methodFilter);
68   }
69
70   public RequestHint(final ThreadReferenceProxyImpl stepThread,
71                      final SuspendContextImpl suspendContext,
72                      @MagicConstant (intValues = {StepRequest.STEP_INTO, StepRequest.STEP_OVER, StepRequest.STEP_OUT}) int depth) {
73     this(stepThread, suspendContext, StepRequest.STEP_LINE, depth, null);
74   }
75
76   private RequestHint(final ThreadReferenceProxyImpl stepThread,
77                       final SuspendContextImpl suspendContext,
78                       @MagicConstant (intValues = {StepRequest.STEP_MIN, StepRequest.STEP_LINE}) int stepSize,
79                       @MagicConstant (intValues = {StepRequest.STEP_INTO, StepRequest.STEP_OVER, StepRequest.STEP_OUT}) int depth,
80                       @Nullable MethodFilter methodFilter) {
81     mySize = stepSize;
82     myDepth = depth;
83     myMethodFilter = methodFilter;
84
85     int frameCount = 0;
86     SourcePosition position = null;
87     try {
88       frameCount = stepThread.frameCount();
89
90       position = ContextUtil.getSourcePosition(new StackFrameContext() {
91         public StackFrameProxy getFrameProxy() {
92           try {
93             return stepThread.frame(0);
94           }
95           catch (EvaluateException e) {
96             LOG.debug(e);
97             return null;
98           }
99         }
100
101         @NotNull
102         public DebugProcess getDebugProcess() {
103           return suspendContext.getDebugProcess();
104         }
105       });
106     }
107     catch (Exception e) {
108       LOG.info(e);
109     }
110     finally {
111       myFrameCount = frameCount;
112       myPosition = position;
113     }
114   }
115
116   public void setIgnoreFilters(boolean ignoreFilters) {
117     myIgnoreFilters = ignoreFilters;
118   }
119
120   public void setResetIgnoreFilters(boolean resetIgnoreFilters) {
121     myResetIgnoreFilters = resetIgnoreFilters;
122   }
123
124   public boolean isResetIgnoreFilters() {
125     return myResetIgnoreFilters;
126   }
127
128   public void setRestoreBreakpoints(boolean restoreBreakpoints) {
129     myRestoreBreakpoints = restoreBreakpoints;
130   }
131
132   public boolean isRestoreBreakpoints() {
133     return myRestoreBreakpoints;
134   }
135
136   public boolean isIgnoreFilters() {
137     return myIgnoreFilters;
138   }
139
140   @MagicConstant (intValues = {StepRequest.STEP_MIN, StepRequest.STEP_LINE})
141   public int getSize() {
142     return mySize;
143   }
144
145   @MagicConstant (intValues = {StepRequest.STEP_INTO, StepRequest.STEP_OVER, StepRequest.STEP_OUT})
146   public int getDepth() {
147     return myDepth;
148   }
149
150   @Nullable
151   public MethodFilter getMethodFilter() {
152     return myMethodFilter;
153   }
154
155   public boolean wasStepTargetMethodMatched() {
156     return myMethodFilter instanceof BreakpointStepMethodFilter || myTargetMethodMatched;
157   }
158
159   private boolean isTheSameFrame(SuspendContextImpl context) {
160     if (mySteppedOut) return false;
161     final ThreadReferenceProxyImpl contextThread = context.getThread();
162     if (contextThread != null) {
163       try {
164         int currentDepth = contextThread.frameCount();
165         if (currentDepth < myFrameCount) mySteppedOut = true;
166         return currentDepth == myFrameCount;
167       }
168       catch (EvaluateException ignored) {
169       }
170     }
171     return false;
172   }
173
174   private boolean isOnTheSameLine(SourcePosition locationPosition) {
175     if (myMethodFilter == null) {
176       return myPosition.getLine() == locationPosition.getLine();
177     }
178     else {
179       Range<Integer> exprLines = myMethodFilter.getCallingExpressionLines();
180       return exprLines != null && exprLines.isWithin(locationPosition.getLine());
181     }
182   }
183
184   private int reached(MethodFilter filter, SuspendContextImpl context) {
185     if (filter instanceof ActionMethodFilter) {
186       return ((ActionMethodFilter)filter).onReached(context, this);
187     }
188     return STOP;
189   }
190
191   public int getNextStepDepth(final SuspendContextImpl context) {
192     try {
193       final StackFrameProxyImpl frameProxy = context.getFrameProxy();
194
195       // smart step feature stop check
196       if (myMethodFilter != null &&
197           frameProxy != null &&
198           !(myMethodFilter instanceof BreakpointStepMethodFilter) &&
199           myMethodFilter.locationMatches(context.getDebugProcess(), frameProxy.location()) &&
200           !isTheSameFrame(context)
201         ) {
202         myTargetMethodMatched = true;
203         return reached(myMethodFilter, context);
204       }
205
206       if ((myDepth == StepRequest.STEP_OVER || myDepth == StepRequest.STEP_INTO) && myPosition != null) {
207         SourcePosition locationPosition = ContextUtil.getSourcePosition(context);
208         if (locationPosition != null) {
209           Integer resultDepth = ApplicationManager.getApplication().runReadAction(new Computable<Integer>() {
210             public Integer compute() {
211               if (myPosition.getFile().equals(locationPosition.getFile()) && isTheSameFrame(context) && !mySteppedOut) {
212                 return isOnTheSameLine(locationPosition) ? myDepth : STOP;
213               }
214               return null;
215             }
216           });
217           if (resultDepth != null) {
218             return resultDepth.intValue();
219           }
220         }
221       }
222
223       // Now check filters
224
225       final DebuggerSettings settings = DebuggerSettings.getInstance();
226
227       if ((myMethodFilter != null || (settings.SKIP_SYNTHETIC_METHODS && !myIgnoreFilters))&& frameProxy != null) {
228         final Location location = frameProxy.location();
229         if (location != null) {
230           if (DebuggerUtils.isSynthetic(location.method())) {
231             return myDepth;
232           }
233         }
234       }
235
236       if (!myIgnoreFilters) {
237         if(settings.SKIP_GETTERS) {
238           boolean isGetter = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>(){
239             public Boolean compute() {
240               PsiElement contextElement = ContextUtil.getContextElement(context);
241               return contextElement != null && DebuggerUtils.isInsideSimpleGetter(contextElement);
242             }
243           }).booleanValue();
244
245           if(isGetter) {
246             return StepRequest.STEP_OUT;
247           }
248         }
249
250         if (frameProxy != null) {
251           if (settings.SKIP_CONSTRUCTORS) {
252             final Location location = frameProxy.location();
253             if (location != null) {
254               final Method method = location.method();
255               if (method != null && method.isConstructor()) {
256                 return StepRequest.STEP_OUT;
257               }
258             }
259           }
260
261           if (settings.SKIP_CLASSLOADERS) {
262             final Location location = frameProxy.location();
263             if (location != null && DebuggerUtilsEx.isAssignableFrom("java.lang.ClassLoader", location.declaringType())) {
264               return StepRequest.STEP_OUT;
265             }
266           }
267         }
268
269         for (ExtraSteppingFilter filter : ExtraSteppingFilter.EP_NAME.getExtensions()) {
270           try {
271             if (filter.isApplicable(context)) return filter.getStepRequestDepth(context);
272           }
273           catch (Exception e) {LOG.error(e);}
274           catch (AssertionError e) {LOG.error(e);}
275         }
276       }
277       // smart step feature
278       if (myMethodFilter != null && !mySteppedOut) {
279         return StepRequest.STEP_OUT;
280       }
281     }
282     catch (VMDisconnectedException ignored) {
283     }
284     catch (EvaluateException e) {
285       LOG.error(e);
286     }
287     return STOP;
288   }
289
290 }