]> git.saurik.com Git - apple/javascriptcore.git/blob - inspector/ScriptDebugServer.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / inspector / ScriptDebugServer.cpp
1 /*
2 * Copyright (C) 2008, 2009, 2013, 2014 Apple Inc. All rights reserved.
3 * Copyright (C) 2010-2011 Google Inc. All rights reserved.
4 * Copyright (C) 2013 University of Washington. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
16 * its contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "ScriptDebugServer.h"
33
34 #include "DebuggerCallFrame.h"
35 #include "DebuggerScope.h"
36 #include "Exception.h"
37 #include "JSJavaScriptCallFrame.h"
38 #include "JSLock.h"
39 #include "JavaScriptCallFrame.h"
40 #include "ScriptValue.h"
41 #include "SourceProvider.h"
42 #include <wtf/NeverDestroyed.h>
43 #include <wtf/TemporaryChange.h>
44 #include <wtf/text/WTFString.h>
45
46 using namespace JSC;
47
48 namespace Inspector {
49
50 ScriptDebugServer::ScriptDebugServer(bool isInWorkerThread)
51 : Debugger(isInWorkerThread)
52 {
53 }
54
55 ScriptDebugServer::~ScriptDebugServer()
56 {
57 }
58
59 JSC::BreakpointID ScriptDebugServer::setBreakpoint(JSC::SourceID sourceID, const ScriptBreakpoint& scriptBreakpoint, unsigned* actualLineNumber, unsigned* actualColumnNumber)
60 {
61 if (!sourceID)
62 return JSC::noBreakpointID;
63
64 JSC::Breakpoint breakpoint(sourceID, scriptBreakpoint.lineNumber, scriptBreakpoint.columnNumber, scriptBreakpoint.condition, scriptBreakpoint.autoContinue);
65 JSC::BreakpointID id = Debugger::setBreakpoint(breakpoint, *actualLineNumber, *actualColumnNumber);
66 if (id != JSC::noBreakpointID && !scriptBreakpoint.actions.isEmpty()) {
67 #ifndef NDEBUG
68 BreakpointIDToActionsMap::iterator it = m_breakpointIDToActions.find(id);
69 ASSERT(it == m_breakpointIDToActions.end());
70 #endif
71 const BreakpointActions& actions = scriptBreakpoint.actions;
72 m_breakpointIDToActions.set(id, actions);
73 }
74 return id;
75 }
76
77 void ScriptDebugServer::removeBreakpoint(JSC::BreakpointID id)
78 {
79 ASSERT(id != JSC::noBreakpointID);
80 BreakpointIDToActionsMap::iterator it = m_breakpointIDToActions.find(id);
81 if (it != m_breakpointIDToActions.end())
82 m_breakpointIDToActions.remove(it);
83
84 Debugger::removeBreakpoint(id);
85 }
86
87 bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& breakpointAction)
88 {
89 DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame();
90
91 switch (breakpointAction.type) {
92 case ScriptBreakpointActionTypeLog: {
93 dispatchBreakpointActionLog(debuggerCallFrame->exec(), breakpointAction.data);
94 break;
95 }
96 case ScriptBreakpointActionTypeEvaluate: {
97 NakedPtr<Exception> exception;
98 debuggerCallFrame->evaluate(breakpointAction.data, exception);
99 if (exception)
100 reportException(debuggerCallFrame->exec(), exception);
101 break;
102 }
103 case ScriptBreakpointActionTypeSound:
104 dispatchBreakpointActionSound(debuggerCallFrame->exec(), breakpointAction.identifier);
105 break;
106 case ScriptBreakpointActionTypeProbe: {
107 NakedPtr<Exception> exception;
108 JSValue result = debuggerCallFrame->evaluate(breakpointAction.data, exception);
109 if (exception)
110 reportException(debuggerCallFrame->exec(), exception);
111
112 JSC::ExecState* state = debuggerCallFrame->scope()->globalObject()->globalExec();
113 Deprecated::ScriptValue wrappedResult = Deprecated::ScriptValue(state->vm(), exception ? exception->value() : result);
114 dispatchBreakpointActionProbe(state, breakpointAction, wrappedResult);
115 break;
116 }
117 default:
118 ASSERT_NOT_REACHED();
119 }
120
121 return true;
122 }
123
124 void ScriptDebugServer::clearBreakpoints()
125 {
126 Debugger::clearBreakpoints();
127 m_breakpointIDToActions.clear();
128 }
129
130 void ScriptDebugServer::dispatchDidPause(ScriptDebugListener* listener)
131 {
132 ASSERT(isPaused());
133 DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame();
134 JSGlobalObject* globalObject = debuggerCallFrame->scope()->globalObject();
135 JSC::ExecState* state = globalObject->globalExec();
136 RefPtr<JavaScriptCallFrame> javaScriptCallFrame = JavaScriptCallFrame::create(debuggerCallFrame);
137 JSValue jsCallFrame = toJS(state, globalObject, javaScriptCallFrame.get());
138
139 listener->didPause(state, Deprecated::ScriptValue(state->vm(), jsCallFrame), exceptionOrCaughtValue(state));
140 }
141
142 void ScriptDebugServer::dispatchBreakpointActionLog(ExecState* exec, const String& message)
143 {
144 if (m_callingListeners)
145 return;
146
147 ListenerSet& listeners = getListeners();
148 if (listeners.isEmpty())
149 return;
150
151 TemporaryChange<bool> change(m_callingListeners, true);
152
153 Vector<ScriptDebugListener*> listenersCopy;
154 copyToVector(listeners, listenersCopy);
155 for (auto* listener : listenersCopy)
156 listener->breakpointActionLog(exec, message);
157 }
158
159 void ScriptDebugServer::dispatchBreakpointActionSound(ExecState*, int breakpointActionIdentifier)
160 {
161 if (m_callingListeners)
162 return;
163
164 ListenerSet& listeners = getListeners();
165 if (listeners.isEmpty())
166 return;
167
168 TemporaryChange<bool> change(m_callingListeners, true);
169
170 Vector<ScriptDebugListener*> listenersCopy;
171 copyToVector(listeners, listenersCopy);
172 for (auto* listener : listenersCopy)
173 listener->breakpointActionSound(breakpointActionIdentifier);
174 }
175
176 void ScriptDebugServer::dispatchBreakpointActionProbe(ExecState* exec, const ScriptBreakpointAction& action, const Deprecated::ScriptValue& sampleValue)
177 {
178 if (m_callingListeners)
179 return;
180
181 ListenerSet& listeners = getListeners();
182 if (listeners.isEmpty())
183 return;
184
185 TemporaryChange<bool> change(m_callingListeners, true);
186
187 unsigned sampleId = m_nextProbeSampleId++;
188
189 Vector<ScriptDebugListener*> listenersCopy;
190 copyToVector(listeners, listenersCopy);
191 for (auto* listener : listenersCopy)
192 listener->breakpointActionProbe(exec, action, m_currentProbeBatchId, sampleId, sampleValue);
193 }
194
195 void ScriptDebugServer::dispatchDidContinue(ScriptDebugListener* listener)
196 {
197 listener->didContinue();
198 }
199
200 void ScriptDebugServer::dispatchDidParseSource(const ListenerSet& listeners, SourceProvider* sourceProvider, bool isContentScript)
201 {
202 JSC::SourceID sourceID = sourceProvider->asID();
203
204 ScriptDebugListener::Script script;
205 script.url = sourceProvider->url();
206 script.source = sourceProvider->source();
207 script.startLine = sourceProvider->startPosition().m_line.zeroBasedInt();
208 script.startColumn = sourceProvider->startPosition().m_column.zeroBasedInt();
209 script.isContentScript = isContentScript;
210
211 int sourceLength = script.source.length();
212 int lineCount = 1;
213 int lastLineStart = 0;
214 for (int i = 0; i < sourceLength; ++i) {
215 if (script.source[i] == '\n') {
216 lineCount += 1;
217 lastLineStart = i + 1;
218 }
219 }
220
221 script.endLine = script.startLine + lineCount - 1;
222 if (lineCount == 1)
223 script.endColumn = script.startColumn + sourceLength;
224 else
225 script.endColumn = sourceLength - lastLineStart;
226
227 Vector<ScriptDebugListener*> copy;
228 copyToVector(listeners, copy);
229 for (size_t i = 0; i < copy.size(); ++i)
230 copy[i]->didParseSource(sourceID, script);
231 }
232
233 void ScriptDebugServer::dispatchFailedToParseSource(const ListenerSet& listeners, SourceProvider* sourceProvider, int errorLine, const String& errorMessage)
234 {
235 String url = sourceProvider->url();
236 const String& data = sourceProvider->source();
237 int firstLine = sourceProvider->startPosition().m_line.oneBasedInt();
238
239 Vector<ScriptDebugListener*> copy;
240 copyToVector(listeners, copy);
241 for (size_t i = 0; i < copy.size(); ++i)
242 copy[i]->failedToParseSource(url, data, firstLine, errorLine, errorMessage);
243 }
244
245 void ScriptDebugServer::sourceParsed(ExecState* exec, SourceProvider* sourceProvider, int errorLine, const String& errorMessage)
246 {
247 if (m_callingListeners)
248 return;
249
250 ListenerSet& listeners = getListeners();
251 if (listeners.isEmpty())
252 return;
253
254 TemporaryChange<bool> change(m_callingListeners, true);
255
256 bool isError = errorLine != -1;
257 if (isError)
258 dispatchFailedToParseSource(listeners, sourceProvider, errorLine, errorMessage);
259 else
260 dispatchDidParseSource(listeners, sourceProvider, isContentScript(exec));
261 }
262
263 void ScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallback callback)
264 {
265 if (m_callingListeners)
266 return;
267
268 TemporaryChange<bool> change(m_callingListeners, true);
269
270 ListenerSet& listeners = getListeners();
271 if (!listeners.isEmpty())
272 dispatchFunctionToListeners(listeners, callback);
273 }
274
275 void ScriptDebugServer::dispatchFunctionToListeners(const ListenerSet& listeners, JavaScriptExecutionCallback callback)
276 {
277 Vector<ScriptDebugListener*> copy;
278 copyToVector(listeners, copy);
279 for (size_t i = 0; i < copy.size(); ++i)
280 (this->*callback)(copy[i]);
281 }
282
283 void ScriptDebugServer::notifyDoneProcessingDebuggerEvents()
284 {
285 m_doneProcessingDebuggerEvents = true;
286 }
287
288 void ScriptDebugServer::handleBreakpointHit(JSC::JSGlobalObject* globalObject, const JSC::Breakpoint& breakpoint)
289 {
290 ASSERT(isAttached(globalObject));
291
292 m_currentProbeBatchId++;
293
294 BreakpointIDToActionsMap::iterator it = m_breakpointIDToActions.find(breakpoint.id);
295 if (it != m_breakpointIDToActions.end()) {
296 BreakpointActions actions = it->value;
297 for (size_t i = 0; i < actions.size(); ++i) {
298 if (!evaluateBreakpointAction(actions[i]))
299 return;
300 if (!isAttached(globalObject))
301 return;
302 }
303 }
304 }
305
306 void ScriptDebugServer::handleExceptionInBreakpointCondition(JSC::ExecState* exec, JSC::Exception* exception) const
307 {
308 reportException(exec, exception);
309 }
310
311 void ScriptDebugServer::handlePause(JSGlobalObject* vmEntryGlobalObject, Debugger::ReasonForPause)
312 {
313 dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidPause);
314 didPause(vmEntryGlobalObject);
315
316 m_doneProcessingDebuggerEvents = false;
317 runEventLoopWhilePaused();
318
319 didContinue(vmEntryGlobalObject);
320 dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidContinue);
321 }
322
323 const BreakpointActions& ScriptDebugServer::getActionsForBreakpoint(JSC::BreakpointID breakpointID)
324 {
325 ASSERT(breakpointID != JSC::noBreakpointID);
326
327 if (m_breakpointIDToActions.contains(breakpointID))
328 return m_breakpointIDToActions.find(breakpointID)->value;
329
330 static NeverDestroyed<BreakpointActions> emptyActionVector = BreakpointActions();
331 return emptyActionVector;
332 }
333
334 Deprecated::ScriptValue ScriptDebugServer::exceptionOrCaughtValue(JSC::ExecState* state)
335 {
336 if (reasonForPause() == PausedForException)
337 return Deprecated::ScriptValue(state->vm(), currentException());
338
339 RefPtr<DebuggerCallFrame> debuggerCallFrame = currentDebuggerCallFrame();
340 while (debuggerCallFrame) {
341 DebuggerScope* scope = debuggerCallFrame->scope();
342 if (scope->isCatchScope())
343 return Deprecated::ScriptValue(state->vm(), scope->caughtValue());
344 debuggerCallFrame = debuggerCallFrame->callerFrame();
345 }
346
347 return Deprecated::ScriptValue();
348 }
349
350 } // namespace Inspector