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
34namespace JSC {
35
36class ExecState;
37class JSGlobalObject;
38class SourceProvider;
39class VM;
40
41typedef ExecState CallFrame;
42
43class JS_EXPORT_PRIVATE Debugger {
44public:
45    Debugger(bool isInWorkerThread = false);
46    virtual ~Debugger();
47
48    JSC::DebuggerCallFrame* currentDebuggerCallFrame() const;
49    bool hasHandlerForExceptionCallback() const
50    {
51        ASSERT(m_reasonForPause == PausedForException);
52        return m_hasHandlerForExceptionCallback;
53    }
54    JSValue currentException()
55    {
56        ASSERT(m_reasonForPause == PausedForException);
57        return m_currentException;
58    }
59
60    bool needsExceptionCallbacks() const { return m_pauseOnExceptionsState != DontPauseOnExceptions; }
61
62    void attach(JSGlobalObject*);
63    enum ReasonForDetach {
64        TerminatingDebuggingSession,
65        GlobalObjectIsDestructing
66    };
67    virtual void detach(JSGlobalObject*, ReasonForDetach);
68
69    BreakpointID setBreakpoint(Breakpoint, unsigned& actualLine, unsigned& actualColumn);
70    void removeBreakpoint(BreakpointID);
71    void clearBreakpoints();
72    void setBreakpointsActivated(bool);
73    void activateBreakpoints() { setBreakpointsActivated(true); }
74    void deactivateBreakpoints() { setBreakpointsActivated(false); }
75
76    enum PauseOnExceptionsState {
77        DontPauseOnExceptions,
78        PauseOnAllExceptions,
79        PauseOnUncaughtExceptions
80    };
81    PauseOnExceptionsState pauseOnExceptionsState() const { return m_pauseOnExceptionsState; }
82    void setPauseOnExceptionsState(PauseOnExceptionsState);
83
84    void setPauseOnNextStatement(bool);
85    void breakProgram();
86    void continueProgram();
87    void stepIntoStatement();
88    void stepOverStatement();
89    void stepOutOfFunction();
90
91    bool isPaused() { return m_isPaused; }
92    bool isStepping() const { return m_steppingMode == SteppingModeEnabled; }
93
94    virtual void sourceParsed(ExecState*, SourceProvider*, int errorLineNumber, const WTF::String& errorMessage) = 0;
95
96    void exception(CallFrame*, JSValue exceptionValue, bool hasHandler);
97    void atStatement(CallFrame*);
98    void callEvent(CallFrame*);
99    void returnEvent(CallFrame*);
100    void willExecuteProgram(CallFrame*);
101    void didExecuteProgram(CallFrame*);
102    void didReachBreakpoint(CallFrame*);
103
104    void recompileAllJSFunctions(VM*);
105
106    void registerCodeBlock(CodeBlock*);
107
108protected:
109    virtual bool needPauseHandling(JSGlobalObject*) { return false; }
110    virtual void handleBreakpointHit(const Breakpoint&) { }
111    virtual void handleExceptionInBreakpointCondition(ExecState*, JSValue exception) const { UNUSED_PARAM(exception); }
112
113    enum ReasonForPause {
114        NotPaused,
115        PausedForException,
116        PausedAtStatement,
117        PausedAfterCall,
118        PausedBeforeReturn,
119        PausedAtStartOfProgram,
120        PausedAtEndOfProgram,
121        PausedForBreakpoint
122    };
123
124    virtual void handlePause(ReasonForPause, JSGlobalObject*) { }
125    virtual void notifyDoneProcessingDebuggerEvents() { }
126
127private:
128    typedef HashMap<BreakpointID, Breakpoint*> BreakpointIDToBreakpointMap;
129
130    typedef HashMap<unsigned, RefPtr<BreakpointsList>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> LineToBreakpointsMap;
131    typedef HashMap<SourceID, LineToBreakpointsMap, WTF::IntHash<SourceID>, WTF::UnsignedWithZeroKeyHashTraits<SourceID>> SourceIDToBreakpointsMap;
132
133    class ClearCodeBlockDebuggerRequestsFunctor;
134    class ClearDebuggerRequestsFunctor;
135    class SetSteppingModeFunctor;
136    class ToggleBreakpointFunctor;
137
138    class PauseReasonDeclaration {
139    public:
140        PauseReasonDeclaration(Debugger& debugger, ReasonForPause reason)
141            : m_debugger(debugger)
142        {
143            m_debugger.m_reasonForPause = reason;
144        }
145
146        ~PauseReasonDeclaration()
147        {
148            m_debugger.m_reasonForPause = NotPaused;
149        }
150    private:
151        Debugger& m_debugger;
152    };
153
154    bool hasBreakpoint(SourceID, const TextPosition&, Breakpoint* hitBreakpoint);
155
156    void updateNeedForOpDebugCallbacks();
157
158    // These update functions are only needed because our current breakpoints are
159    // key'ed off the source position instead of the bytecode PC. This ensures
160    // that we don't break on the same line more than once. Once we switch to a
161    // bytecode PC key'ed breakpoint, we will not need these anymore and should
162    // be able to remove them.
163    void updateCallFrame(JSC::CallFrame*);
164    void updateCallFrameAndPauseIfNeeded(JSC::CallFrame*);
165    void pauseIfNeeded(JSC::CallFrame*);
166
167    enum SteppingMode {
168        SteppingModeDisabled,
169        SteppingModeEnabled
170    };
171    void setSteppingMode(SteppingMode);
172
173    enum BreakpointState {
174        BreakpointDisabled,
175        BreakpointEnabled
176    };
177    void toggleBreakpoint(CodeBlock*, Breakpoint&, BreakpointState);
178    void applyBreakpoints(CodeBlock*);
179    void toggleBreakpoint(Breakpoint&, BreakpointState);
180
181    void clearDebuggerRequests(JSGlobalObject*);
182
183    template<typename Functor> inline void forEachCodeBlock(Functor&);
184
185    VM* m_vm;
186    HashSet<JSGlobalObject*> m_globalObjects;
187
188    PauseOnExceptionsState m_pauseOnExceptionsState;
189    bool m_pauseOnNextStatement : 1;
190    bool m_isPaused : 1;
191    bool m_breakpointsActivated : 1;
192    bool m_hasHandlerForExceptionCallback : 1;
193    bool m_isInWorkerThread : 1;
194    SteppingMode m_steppingMode : 1;
195
196    ReasonForPause m_reasonForPause;
197    JSValue m_currentException;
198    CallFrame* m_pauseOnCallFrame;
199    CallFrame* m_currentCallFrame;
200    unsigned m_lastExecutedLine;
201    SourceID m_lastExecutedSourceID;
202
203    BreakpointID m_topBreakpointID;
204    BreakpointIDToBreakpointMap m_breakpointIDToBreakpoint;
205    SourceIDToBreakpointsMap m_sourceIDToBreakpoints;
206
207    RefPtr<JSC::DebuggerCallFrame> m_currentDebuggerCallFrame;
208
209    friend class DebuggerCallFrameScope;
210    friend class TemporaryPausedState;
211    friend class LLIntOffsetsExtractor;
212};
213
214} // namespace JSC
215
216#endif // Debugger_h
217