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