Web Inspector: Pause on exceptions should show the actual exception
[WebKit-https.git] / Source / JavaScriptCore / debugger / Debugger.h
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4  *  Copyright (C) 2008, 2009, 2013, 2014 Apple Inc. All rights reserved.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifndef Debugger_h
23 #define Debugger_h
24
25 #include "Breakpoint.h"
26 #include "DebuggerCallFrame.h"
27 #include "DebuggerPrimitives.h"
28 #include "JSCJSValue.h"
29 #include <wtf/HashMap.h>
30 #include <wtf/HashSet.h>
31 #include <wtf/RefPtr.h>
32 #include <wtf/text/TextPosition.h>
33
34 namespace JSC {
35
36 class CodeBlock;
37 class ExecState;
38 class JSGlobalObject;
39 class SourceProvider;
40 class VM;
41
42 typedef ExecState CallFrame;
43
44 class JS_EXPORT_PRIVATE Debugger {
45 public:
46     Debugger(bool isInWorkerThread = false);
47     virtual ~Debugger();
48
49     JSC::DebuggerCallFrame* currentDebuggerCallFrame() const;
50     bool hasHandlerForExceptionCallback() const
51     {
52         ASSERT(m_reasonForPause == PausedForException);
53         return m_hasHandlerForExceptionCallback;
54     }
55     JSValue currentException()
56     {
57         ASSERT(m_reasonForPause == PausedForException);
58         return m_currentException;
59     }
60
61     bool needsExceptionCallbacks() const { return m_pauseOnExceptionsState != DontPauseOnExceptions; }
62
63     void attach(JSGlobalObject*);
64     enum ReasonForDetach {
65         TerminatingDebuggingSession,
66         GlobalObjectIsDestructing
67     };
68     virtual void detach(JSGlobalObject*, ReasonForDetach);
69
70     BreakpointID setBreakpoint(Breakpoint, unsigned& actualLine, unsigned& actualColumn);
71     void removeBreakpoint(BreakpointID);
72     void clearBreakpoints();
73     void setBreakpointsActivated(bool);
74     void activateBreakpoints() { setBreakpointsActivated(true); }
75     void deactivateBreakpoints() { setBreakpointsActivated(false); }
76
77     enum PauseOnExceptionsState {
78         DontPauseOnExceptions,
79         PauseOnAllExceptions,
80         PauseOnUncaughtExceptions
81     };
82     PauseOnExceptionsState pauseOnExceptionsState() const { return m_pauseOnExceptionsState; }
83     void setPauseOnExceptionsState(PauseOnExceptionsState);
84
85     void setPauseOnNextStatement(bool);
86     void breakProgram();
87     void continueProgram();
88     void stepIntoStatement();
89     void stepOverStatement();
90     void stepOutOfFunction();
91
92     bool isPaused() { return m_isPaused; }
93     bool isStepping() const { return m_steppingMode == SteppingModeEnabled; }
94
95     virtual void sourceParsed(ExecState*, SourceProvider*, int errorLineNumber, const WTF::String& errorMessage) = 0;
96
97     void exception(CallFrame*, JSValue exceptionValue, bool hasHandler);
98     void atStatement(CallFrame*);
99     void callEvent(CallFrame*);
100     void returnEvent(CallFrame*);
101     void willExecuteProgram(CallFrame*);
102     void didExecuteProgram(CallFrame*);
103     void didReachBreakpoint(CallFrame*);
104
105     void recompileAllJSFunctions(VM*);
106
107     void registerCodeBlock(CodeBlock*);
108
109 protected:
110     virtual bool needPauseHandling(JSGlobalObject*) { return false; }
111     virtual void handleBreakpointHit(const Breakpoint&) { }
112     virtual void handleExceptionInBreakpointCondition(ExecState*, JSValue exception) const { UNUSED_PARAM(exception); }
113
114     enum ReasonForPause {
115         NotPaused,
116         PausedForException,
117         PausedAtStatement,
118         PausedAfterCall,
119         PausedBeforeReturn,
120         PausedAtStartOfProgram,
121         PausedAtEndOfProgram,
122         PausedForBreakpoint
123     };
124
125     ReasonForPause reasonForPause() const { return m_reasonForPause; }
126
127     virtual void handlePause(ReasonForPause, JSGlobalObject*) { }
128     virtual void notifyDoneProcessingDebuggerEvents() { }
129
130 private:
131     typedef HashMap<BreakpointID, Breakpoint*> BreakpointIDToBreakpointMap;
132
133     typedef HashMap<unsigned, RefPtr<BreakpointsList>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> LineToBreakpointsMap;
134     typedef HashMap<SourceID, LineToBreakpointsMap, WTF::IntHash<SourceID>, WTF::UnsignedWithZeroKeyHashTraits<SourceID>> SourceIDToBreakpointsMap;
135
136     class ClearCodeBlockDebuggerRequestsFunctor;
137     class ClearDebuggerRequestsFunctor;
138     class SetSteppingModeFunctor;
139     class ToggleBreakpointFunctor;
140
141     class PauseReasonDeclaration {
142     public:
143         PauseReasonDeclaration(Debugger& debugger, ReasonForPause reason)
144             : m_debugger(debugger)
145         {
146             m_debugger.m_reasonForPause = reason;
147         }
148
149         ~PauseReasonDeclaration()
150         {
151             m_debugger.m_reasonForPause = NotPaused;
152         }
153     private:
154         Debugger& m_debugger;
155     };
156
157     bool hasBreakpoint(SourceID, const TextPosition&, Breakpoint* hitBreakpoint);
158
159     void updateNeedForOpDebugCallbacks();
160
161     // These update functions are only needed because our current breakpoints are
162     // key'ed off the source position instead of the bytecode PC. This ensures
163     // that we don't break on the same line more than once. Once we switch to a
164     // bytecode PC key'ed breakpoint, we will not need these anymore and should
165     // be able to remove them.
166     void updateCallFrame(JSC::CallFrame*);
167     void updateCallFrameAndPauseIfNeeded(JSC::CallFrame*);
168     void pauseIfNeeded(JSC::CallFrame*);
169
170     enum SteppingMode {
171         SteppingModeDisabled,
172         SteppingModeEnabled
173     };
174     void setSteppingMode(SteppingMode);
175
176     enum BreakpointState {
177         BreakpointDisabled,
178         BreakpointEnabled
179     };
180     void toggleBreakpoint(CodeBlock*, Breakpoint&, BreakpointState);
181     void applyBreakpoints(CodeBlock*);
182     void toggleBreakpoint(Breakpoint&, BreakpointState);
183
184     void clearDebuggerRequests(JSGlobalObject*);
185
186     template<typename Functor> inline void forEachCodeBlock(Functor&);
187
188     VM* m_vm;
189     HashSet<JSGlobalObject*> m_globalObjects;
190
191     PauseOnExceptionsState m_pauseOnExceptionsState;
192     bool m_pauseOnNextStatement : 1;
193     bool m_isPaused : 1;
194     bool m_breakpointsActivated : 1;
195     bool m_hasHandlerForExceptionCallback : 1;
196     bool m_isInWorkerThread : 1;
197     unsigned m_steppingMode : 1; // SteppingMode
198
199     ReasonForPause m_reasonForPause;
200     JSValue m_currentException;
201     CallFrame* m_pauseOnCallFrame;
202     CallFrame* m_currentCallFrame;
203     unsigned m_lastExecutedLine;
204     SourceID m_lastExecutedSourceID;
205
206     BreakpointID m_topBreakpointID;
207     BreakpointIDToBreakpointMap m_breakpointIDToBreakpoint;
208     SourceIDToBreakpointsMap m_sourceIDToBreakpoints;
209
210     RefPtr<JSC::DebuggerCallFrame> m_currentDebuggerCallFrame;
211
212     friend class DebuggerPausedScope;
213     friend class TemporaryPausedState;
214     friend class LLIntOffsetsExtractor;
215 };
216
217 } // namespace JSC
218
219 #endif // Debugger_h