summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/debugger/Debugger.h
blob: 2e91aafbe277723f406d157d151dcf309cf3a5fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/*
 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
 *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
 *  Copyright (C) 2008, 2009, 2013, 2014 Apple Inc. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifndef Debugger_h
#define Debugger_h

#include "Breakpoint.h"
#include "CallData.h"
#include "DebuggerCallFrame.h"
#include "DebuggerPrimitives.h"
#include "JSCJSValue.h"
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
#include <wtf/RefPtr.h>
#include <wtf/text/TextPosition.h>

namespace JSC {

class CodeBlock;
class Exception;
class ExecState;
class JSGlobalObject;
class SourceProvider;
class VM;

typedef ExecState CallFrame;

class JS_EXPORT_PRIVATE Debugger {
public:
    Debugger(VM&);
    virtual ~Debugger();

    VM& vm() { return m_vm; }

    JSC::DebuggerCallFrame* currentDebuggerCallFrame() const;
    bool hasHandlerForExceptionCallback() const
    {
        ASSERT(m_reasonForPause == PausedForException);
        return m_hasHandlerForExceptionCallback;
    }
    JSValue currentException()
    {
        ASSERT(m_reasonForPause == PausedForException);
        return m_currentException;
    }

    bool needsExceptionCallbacks() const { return m_pauseOnExceptionsState != DontPauseOnExceptions; }

    enum ReasonForDetach {
        TerminatingDebuggingSession,
        GlobalObjectIsDestructing
    };
    void attach(JSGlobalObject*);
    void detach(JSGlobalObject*, ReasonForDetach);
    bool isAttached(JSGlobalObject*);

    BreakpointID setBreakpoint(Breakpoint, unsigned& actualLine, unsigned& actualColumn);
    void removeBreakpoint(BreakpointID);
    void clearBreakpoints();
    void setBreakpointsActivated(bool);
    void activateBreakpoints() { setBreakpointsActivated(true); }
    void deactivateBreakpoints() { setBreakpointsActivated(false); }

    enum PauseOnExceptionsState {
        DontPauseOnExceptions,
        PauseOnAllExceptions,
        PauseOnUncaughtExceptions
    };
    PauseOnExceptionsState pauseOnExceptionsState() const { return m_pauseOnExceptionsState; }
    void setPauseOnExceptionsState(PauseOnExceptionsState);

    enum ReasonForPause {
        NotPaused,
        PausedForException,
        PausedAtStatement,
        PausedAfterCall,
        PausedBeforeReturn,
        PausedAtStartOfProgram,
        PausedAtEndOfProgram,
        PausedForBreakpoint,
        PausedForDebuggerStatement,
    };
    ReasonForPause reasonForPause() const { return m_reasonForPause; }
    BreakpointID pausingBreakpointID() const { return m_pausingBreakpointID; }

    void setPauseOnNextStatement(bool);
    void breakProgram();
    void continueProgram();
    void stepIntoStatement();
    void stepOverStatement();
    void stepOutOfFunction();

    bool isPaused() const { return m_isPaused; }
    bool isStepping() const { return m_steppingMode == SteppingModeEnabled; }

    bool suppressAllPauses() const { return m_suppressAllPauses; }
    void setSuppressAllPauses(bool suppress) { m_suppressAllPauses = suppress; }

    virtual void sourceParsed(ExecState*, SourceProvider*, int errorLineNumber, const WTF::String& errorMessage) = 0;

    void exception(CallFrame*, JSValue exceptionValue, bool hasCatchHandler);
    void atStatement(CallFrame*);
    void callEvent(CallFrame*);
    void returnEvent(CallFrame*);
    void willExecuteProgram(CallFrame*);
    void didExecuteProgram(CallFrame*);
    void didReachBreakpoint(CallFrame*);

    virtual void recompileAllJSFunctions();

    void registerCodeBlock(CodeBlock*);

    class ProfilingClient {
    public:
        virtual ~ProfilingClient() { }
        virtual bool isAlreadyProfiling() const = 0;
        virtual double willEvaluateScript() = 0;
        virtual void didEvaluateScript(double startTime, ProfilingReason) = 0;
    };

    void setProfilingClient(ProfilingClient*);
    bool hasProfilingClient() const { return m_profilingClient != nullptr; }
    bool isAlreadyProfiling() const { return m_profilingClient && m_profilingClient->isAlreadyProfiling(); }
    double willEvaluateScript();
    void didEvaluateScript(double startTime, ProfilingReason);

protected:
    virtual bool needPauseHandling(JSGlobalObject*) { return false; }
    virtual void handleBreakpointHit(JSGlobalObject*, const Breakpoint&) { }
    virtual void handleExceptionInBreakpointCondition(ExecState*, Exception*) const { }
    virtual void handlePause(JSGlobalObject*, ReasonForPause) { }
    virtual void notifyDoneProcessingDebuggerEvents() { }

private:
    typedef HashMap<BreakpointID, Breakpoint*> BreakpointIDToBreakpointMap;

    typedef HashMap<unsigned, RefPtr<BreakpointsList>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> LineToBreakpointsMap;
    typedef HashMap<SourceID, LineToBreakpointsMap, WTF::IntHash<SourceID>, WTF::UnsignedWithZeroKeyHashTraits<SourceID>> SourceIDToBreakpointsMap;

    class ClearCodeBlockDebuggerRequestsFunctor;
    class ClearDebuggerRequestsFunctor;
    class SetSteppingModeFunctor;
    class ToggleBreakpointFunctor;

    class PauseReasonDeclaration {
    public:
        PauseReasonDeclaration(Debugger& debugger, ReasonForPause reason)
            : m_debugger(debugger)
        {
            m_debugger.m_reasonForPause = reason;
        }

        ~PauseReasonDeclaration()
        {
            m_debugger.m_reasonForPause = NotPaused;
        }
    private:
        Debugger& m_debugger;
    };

    bool hasBreakpoint(SourceID, const TextPosition&, Breakpoint* hitBreakpoint);

    void updateNeedForOpDebugCallbacks();

    // These update functions are only needed because our current breakpoints are
    // key'ed off the source position instead of the bytecode PC. This ensures
    // that we don't break on the same line more than once. Once we switch to a
    // bytecode PC key'ed breakpoint, we will not need these anymore and should
    // be able to remove them.
    void updateCallFrame(JSC::CallFrame*);
    void updateCallFrameAndPauseIfNeeded(JSC::CallFrame*);
    void pauseIfNeeded(JSC::CallFrame*);

    enum SteppingMode {
        SteppingModeDisabled,
        SteppingModeEnabled
    };
    void setSteppingMode(SteppingMode);

    enum BreakpointState {
        BreakpointDisabled,
        BreakpointEnabled
    };
    void toggleBreakpoint(CodeBlock*, Breakpoint&, BreakpointState);
    void applyBreakpoints(CodeBlock*);
    void toggleBreakpoint(Breakpoint&, BreakpointState);

    void clearDebuggerRequests(JSGlobalObject*);

    VM& m_vm;
    HashSet<JSGlobalObject*> m_globalObjects;

    PauseOnExceptionsState m_pauseOnExceptionsState;
    bool m_pauseOnNextStatement : 1;
    bool m_isPaused : 1;
    bool m_breakpointsActivated : 1;
    bool m_hasHandlerForExceptionCallback : 1;
    bool m_suppressAllPauses : 1;
    unsigned m_steppingMode : 1; // SteppingMode

    ReasonForPause m_reasonForPause;
    JSValue m_currentException;
    CallFrame* m_pauseOnCallFrame;
    CallFrame* m_currentCallFrame;
    unsigned m_lastExecutedLine;
    SourceID m_lastExecutedSourceID;

    BreakpointID m_topBreakpointID;
    BreakpointID m_pausingBreakpointID;
    BreakpointIDToBreakpointMap m_breakpointIDToBreakpoint;
    SourceIDToBreakpointsMap m_sourceIDToBreakpoints;

    RefPtr<JSC::DebuggerCallFrame> m_currentDebuggerCallFrame;

    ProfilingClient* m_profilingClient { nullptr };

    friend class DebuggerPausedScope;
    friend class TemporaryPausedState;
    friend class LLIntOffsetsExtractor;
};

} // namespace JSC

#endif // Debugger_h