]> git.saurik.com Git - apple/javascriptcore.git/blame - inspector/ScriptDebugServer.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / inspector / ScriptDebugServer.cpp
CommitLineData
81345200
A
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
81345200 34#include "DebuggerCallFrame.h"
ed1e77d3
A
35#include "DebuggerScope.h"
36#include "Exception.h"
81345200
A
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
46using namespace JSC;
47
48namespace Inspector {
49
50ScriptDebugServer::ScriptDebugServer(bool isInWorkerThread)
51 : Debugger(isInWorkerThread)
81345200
A
52{
53}
54
55ScriptDebugServer::~ScriptDebugServer()
56{
57}
58
59JSC::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
77void 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
87bool 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: {
ed1e77d3 97 NakedPtr<Exception> exception;
81345200
A
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: {
ed1e77d3 107 NakedPtr<Exception> exception;
81345200
A
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();
ed1e77d3 113 Deprecated::ScriptValue wrappedResult = Deprecated::ScriptValue(state->vm(), exception ? exception->value() : result);
81345200
A
114 dispatchBreakpointActionProbe(state, breakpointAction, wrappedResult);
115 break;
116 }
117 default:
118 ASSERT_NOT_REACHED();
119 }
120
121 return true;
122}
123
124void ScriptDebugServer::clearBreakpoints()
125{
126 Debugger::clearBreakpoints();
127 m_breakpointIDToActions.clear();
128}
129
130void 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());
ed1e77d3
A
138
139 listener->didPause(state, Deprecated::ScriptValue(state->vm(), jsCallFrame), exceptionOrCaughtValue(state));
81345200
A
140}
141
142void ScriptDebugServer::dispatchBreakpointActionLog(ExecState* exec, const String& message)
143{
144 if (m_callingListeners)
145 return;
146
ed1e77d3
A
147 ListenerSet& listeners = getListeners();
148 if (listeners.isEmpty())
81345200 149 return;
81345200
A
150
151 TemporaryChange<bool> change(m_callingListeners, true);
152
153 Vector<ScriptDebugListener*> listenersCopy;
ed1e77d3 154 copyToVector(listeners, listenersCopy);
81345200
A
155 for (auto* listener : listenersCopy)
156 listener->breakpointActionLog(exec, message);
157}
158
ed1e77d3 159void ScriptDebugServer::dispatchBreakpointActionSound(ExecState*, int breakpointActionIdentifier)
81345200
A
160{
161 if (m_callingListeners)
162 return;
163
ed1e77d3
A
164 ListenerSet& listeners = getListeners();
165 if (listeners.isEmpty())
81345200 166 return;
81345200
A
167
168 TemporaryChange<bool> change(m_callingListeners, true);
169
170 Vector<ScriptDebugListener*> listenersCopy;
ed1e77d3 171 copyToVector(listeners, listenersCopy);
81345200
A
172 for (auto* listener : listenersCopy)
173 listener->breakpointActionSound(breakpointActionIdentifier);
174}
175
ed1e77d3 176void ScriptDebugServer::dispatchBreakpointActionProbe(ExecState* exec, const ScriptBreakpointAction& action, const Deprecated::ScriptValue& sampleValue)
81345200
A
177{
178 if (m_callingListeners)
179 return;
180
ed1e77d3
A
181 ListenerSet& listeners = getListeners();
182 if (listeners.isEmpty())
81345200 183 return;
81345200
A
184
185 TemporaryChange<bool> change(m_callingListeners, true);
186
ed1e77d3
A
187 unsigned sampleId = m_nextProbeSampleId++;
188
81345200 189 Vector<ScriptDebugListener*> listenersCopy;
ed1e77d3 190 copyToVector(listeners, listenersCopy);
81345200 191 for (auto* listener : listenersCopy)
ed1e77d3 192 listener->breakpointActionProbe(exec, action, m_currentProbeBatchId, sampleId, sampleValue);
81345200
A
193}
194
195void ScriptDebugServer::dispatchDidContinue(ScriptDebugListener* listener)
196{
197 listener->didContinue();
198}
199
200void 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
233void 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
245void ScriptDebugServer::sourceParsed(ExecState* exec, SourceProvider* sourceProvider, int errorLine, const String& errorMessage)
246{
247 if (m_callingListeners)
248 return;
249
ed1e77d3
A
250 ListenerSet& listeners = getListeners();
251 if (listeners.isEmpty())
81345200 252 return;
81345200
A
253
254 TemporaryChange<bool> change(m_callingListeners, true);
255
256 bool isError = errorLine != -1;
257 if (isError)
ed1e77d3 258 dispatchFailedToParseSource(listeners, sourceProvider, errorLine, errorMessage);
81345200 259 else
ed1e77d3 260 dispatchDidParseSource(listeners, sourceProvider, isContentScript(exec));
81345200
A
261}
262
ed1e77d3 263void ScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallback callback)
81345200
A
264{
265 if (m_callingListeners)
266 return;
267
268 TemporaryChange<bool> change(m_callingListeners, true);
269
ed1e77d3
A
270 ListenerSet& listeners = getListeners();
271 if (!listeners.isEmpty())
272 dispatchFunctionToListeners(listeners, callback);
81345200
A
273}
274
ed1e77d3 275void ScriptDebugServer::dispatchFunctionToListeners(const ListenerSet& listeners, JavaScriptExecutionCallback callback)
81345200 276{
ed1e77d3
A
277 Vector<ScriptDebugListener*> copy;
278 copyToVector(listeners, copy);
279 for (size_t i = 0; i < copy.size(); ++i)
280 (this->*callback)(copy[i]);
81345200
A
281}
282
ed1e77d3 283void ScriptDebugServer::notifyDoneProcessingDebuggerEvents()
81345200 284{
ed1e77d3 285 m_doneProcessingDebuggerEvents = true;
81345200
A
286}
287
ed1e77d3 288void ScriptDebugServer::handleBreakpointHit(JSC::JSGlobalObject* globalObject, const JSC::Breakpoint& breakpoint)
81345200 289{
ed1e77d3
A
290 ASSERT(isAttached(globalObject));
291
292 m_currentProbeBatchId++;
293
81345200
A
294 BreakpointIDToActionsMap::iterator it = m_breakpointIDToActions.find(breakpoint.id);
295 if (it != m_breakpointIDToActions.end()) {
ed1e77d3 296 BreakpointActions actions = it->value;
81345200
A
297 for (size_t i = 0; i < actions.size(); ++i) {
298 if (!evaluateBreakpointAction(actions[i]))
299 return;
ed1e77d3
A
300 if (!isAttached(globalObject))
301 return;
81345200
A
302 }
303 }
304}
305
ed1e77d3 306void ScriptDebugServer::handleExceptionInBreakpointCondition(JSC::ExecState* exec, JSC::Exception* exception) const
81345200
A
307{
308 reportException(exec, exception);
309}
310
ed1e77d3 311void ScriptDebugServer::handlePause(JSGlobalObject* vmEntryGlobalObject, Debugger::ReasonForPause)
81345200 312{
ed1e77d3 313 dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidPause);
81345200
A
314 didPause(vmEntryGlobalObject);
315
316 m_doneProcessingDebuggerEvents = false;
317 runEventLoopWhilePaused();
318
319 didContinue(vmEntryGlobalObject);
ed1e77d3 320 dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidContinue);
81345200
A
321}
322
323const 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
ed1e77d3
A
334Deprecated::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 }
81345200 346
ed1e77d3
A
347 return Deprecated::ScriptValue();
348}
349
350} // namespace Inspector