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#pragma once
23
24#include "Breakpoint.h"
25#include "CallData.h"
26#include "DebuggerCallFrame.h"
27#include "DebuggerParseData.h"
28#include "DebuggerPrimitives.h"
29#include "JSCJSValue.h"
30#include <wtf/HashMap.h>
31#include <wtf/HashSet.h>
32#include <wtf/RefPtr.h>
33#include <wtf/text/TextPosition.h>
34
35namespace JSC {
36
37class CodeBlock;
38class Exception;
39class ExecState;
40class JSGlobalObject;
41class SourceProvider;
42class VM;
43
44typedef ExecState CallFrame;
45
46class JS_EXPORT_PRIVATE Debugger {
47public:
48 Debugger(VM&);
49 virtual ~Debugger();
50
51 VM& vm() { return m_vm; }
52
53 JSC::DebuggerCallFrame& currentDebuggerCallFrame();
54 bool hasHandlerForExceptionCallback() const
55 {
56 ASSERT(m_reasonForPause == PausedForException);
57 return m_hasHandlerForExceptionCallback;
58 }
59 JSValue currentException()
60 {
61 ASSERT(m_reasonForPause == PausedForException);
62 return m_currentException;
63 }
64
65 bool needsExceptionCallbacks() const { return m_breakpointsActivated && m_pauseOnExceptionsState != DontPauseOnExceptions; }
66 bool isInteractivelyDebugging() const { return m_breakpointsActivated; }
67
68 enum ReasonForDetach {
69 TerminatingDebuggingSession,
70 GlobalObjectIsDestructing
71 };
72 void attach(JSGlobalObject*);
73 void detach(JSGlobalObject*, ReasonForDetach);
74 bool isAttached(JSGlobalObject*);
75
76 void resolveBreakpoint(Breakpoint&, SourceProvider*);
77 BreakpointID setBreakpoint(Breakpoint&, bool& existing);
78 void removeBreakpoint(BreakpointID);
79 void clearBreakpoints();
80
81 void activateBreakpoints() { setBreakpointsActivated(true); }
82 void deactivateBreakpoints() { setBreakpointsActivated(false); }
83 bool breakpointsActive() const { return m_breakpointsActivated; }
84
85 enum PauseOnExceptionsState {
86 DontPauseOnExceptions,
87 PauseOnAllExceptions,
88 PauseOnUncaughtExceptions
89 };
90 PauseOnExceptionsState pauseOnExceptionsState() const { return m_pauseOnExceptionsState; }
91 void setPauseOnExceptionsState(PauseOnExceptionsState);
92
93 enum ReasonForPause {
94 NotPaused,
95 PausedForException,
96 PausedAtStatement,
97 PausedAtExpression,
98 PausedBeforeReturn,
99 PausedAtEndOfProgram,
100 PausedForBreakpoint,
101 PausedForDebuggerStatement,
102 };
103 ReasonForPause reasonForPause() const { return m_reasonForPause; }
104 BreakpointID pausingBreakpointID() const { return m_pausingBreakpointID; }
105
106 void setPauseOnNextStatement(bool);
107 void breakProgram();
108 void continueProgram();
109 void stepIntoStatement();
110 void stepOverStatement();
111 void stepOutOfFunction();
112
113 bool isBlacklisted(SourceID) const;
114 void addToBlacklist(SourceID);
115 void clearBlacklist();
116
117 bool isPaused() const { return m_isPaused; }
118 bool isStepping() const { return m_steppingMode == SteppingModeEnabled; }
119
120 bool suppressAllPauses() const { return m_suppressAllPauses; }
121 void setSuppressAllPauses(bool suppress) { m_suppressAllPauses = suppress; }
122
123 virtual void sourceParsed(ExecState*, SourceProvider*, int errorLineNumber, const WTF::String& errorMessage) = 0;
124
125 void exception(CallFrame*, JSValue exceptionValue, bool hasCatchHandler);
126 void atStatement(CallFrame*);
127 void atExpression(CallFrame*);
128 void callEvent(CallFrame*);
129 void returnEvent(CallFrame*);
130 void unwindEvent(CallFrame*);
131 void willExecuteProgram(CallFrame*);
132 void didExecuteProgram(CallFrame*);
133 void didReachBreakpoint(CallFrame*);
134
135 virtual void recompileAllJSFunctions();
136
137 void registerCodeBlock(CodeBlock*);
138
139 class ProfilingClient {
140 public:
141 virtual ~ProfilingClient();
142 virtual bool isAlreadyProfiling() const = 0;
143 virtual Seconds willEvaluateScript() = 0;
144 virtual void didEvaluateScript(Seconds startTime, ProfilingReason) = 0;
145 };
146
147 void setProfilingClient(ProfilingClient*);
148 bool hasProfilingClient() const { return m_profilingClient != nullptr; }
149 bool isAlreadyProfiling() const { return m_profilingClient && m_profilingClient->isAlreadyProfiling(); }
150 Seconds willEvaluateScript();
151 void didEvaluateScript(Seconds startTime, ProfilingReason);
152
153protected:
154 virtual void handleBreakpointHit(JSGlobalObject*, const Breakpoint&) { }
155 virtual void handleExceptionInBreakpointCondition(ExecState*, Exception*) const { }
156 virtual void handlePause(JSGlobalObject*, ReasonForPause) { }
157 virtual void notifyDoneProcessingDebuggerEvents() { }
158
159private:
160 typedef HashMap<BreakpointID, Breakpoint*> BreakpointIDToBreakpointMap;
161
162 typedef HashMap<unsigned, RefPtr<BreakpointsList>, WTF::IntHash<int>, WTF::UnsignedWithZeroKeyHashTraits<int>> LineToBreakpointsMap;
163 typedef HashMap<SourceID, LineToBreakpointsMap, WTF::IntHash<SourceID>, WTF::UnsignedWithZeroKeyHashTraits<SourceID>> SourceIDToBreakpointsMap;
164
165 class ClearCodeBlockDebuggerRequestsFunctor;
166 class ClearDebuggerRequestsFunctor;
167 class SetSteppingModeFunctor;
168 class ToggleBreakpointFunctor;
169
170 class PauseReasonDeclaration {
171 public:
172 PauseReasonDeclaration(Debugger& debugger, ReasonForPause reason)
173 : m_debugger(debugger)
174 {
175 m_debugger.m_reasonForPause = reason;
176 }
177
178 ~PauseReasonDeclaration()
179 {
180 m_debugger.m_reasonForPause = NotPaused;
181 }
182 private:
183 Debugger& m_debugger;
184 };
185
186 bool hasBreakpoint(SourceID, const TextPosition&, Breakpoint* hitBreakpoint);
187
188 DebuggerParseData& debuggerParseData(SourceID, SourceProvider*);
189
190 void updateNeedForOpDebugCallbacks();
191
192 // These update functions are only needed because our current breakpoints are
193 // key'ed off the source position instead of the bytecode PC. This ensures
194 // that we don't break on the same line more than once. Once we switch to a
195 // bytecode PC key'ed breakpoint, we will not need these anymore and should
196 // be able to remove them.
197 enum CallFrameUpdateAction { AttemptPause, NoPause };
198 void updateCallFrame(JSC::CallFrame*, CallFrameUpdateAction);
199 void updateCallFrameInternal(JSC::CallFrame*);
200 void pauseIfNeeded(JSC::CallFrame*);
201 void clearNextPauseState();
202
203 enum SteppingMode {
204 SteppingModeDisabled,
205 SteppingModeEnabled
206 };
207 void setSteppingMode(SteppingMode);
208
209 enum BreakpointState {
210 BreakpointDisabled,
211 BreakpointEnabled
212 };
213 void setBreakpointsActivated(bool);
214 void toggleBreakpoint(CodeBlock*, Breakpoint&, BreakpointState);
215 void applyBreakpoints(CodeBlock*);
216 void toggleBreakpoint(Breakpoint&, BreakpointState);
217
218 void clearDebuggerRequests(JSGlobalObject*);
219 void clearParsedData();
220
221 VM& m_vm;
222 HashSet<JSGlobalObject*> m_globalObjects;
223 HashMap<SourceID, DebuggerParseData, WTF::IntHash<SourceID>, WTF::UnsignedWithZeroKeyHashTraits<SourceID>> m_parseDataMap;
224 HashSet<SourceID, WTF::IntHash<SourceID>, WTF::UnsignedWithZeroKeyHashTraits<SourceID>> m_blacklistedScripts;
225
226 PauseOnExceptionsState m_pauseOnExceptionsState;
227 bool m_pauseAtNextOpportunity : 1;
228 bool m_pauseOnStepOut : 1;
229 bool m_pastFirstExpressionInStatement : 1;
230 bool m_isPaused : 1;
231 bool m_breakpointsActivated : 1;
232 bool m_hasHandlerForExceptionCallback : 1;
233 bool m_suppressAllPauses : 1;
234 unsigned m_steppingMode : 1; // SteppingMode
235
236 ReasonForPause m_reasonForPause;
237 JSValue m_currentException;
238 CallFrame* m_pauseOnCallFrame { nullptr };
239 CallFrame* m_currentCallFrame { nullptr };
240 unsigned m_lastExecutedLine;
241 SourceID m_lastExecutedSourceID;
242
243 BreakpointID m_topBreakpointID;
244 BreakpointID m_pausingBreakpointID;
245 BreakpointIDToBreakpointMap m_breakpointIDToBreakpoint;
246 SourceIDToBreakpointsMap m_sourceIDToBreakpoints;
247
248 RefPtr<JSC::DebuggerCallFrame> m_currentDebuggerCallFrame;
249
250 ProfilingClient* m_profilingClient { nullptr };
251
252 friend class DebuggerPausedScope;
253 friend class TemporaryPausedState;
254 friend class LLIntOffsetsExtractor;
255};
256
257} // namespace JSC
258