]> git.saurik.com Git - apple/javascriptcore.git/blame - inspector/ScriptDebugServer.cpp
JavaScriptCore-7600.1.4.9.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
34#if ENABLE(INSPECTOR)
35
36#include "DebuggerCallFrame.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
46using namespace JSC;
47
48namespace Inspector {
49
50ScriptDebugServer::ScriptDebugServer(bool isInWorkerThread)
51 : Debugger(isInWorkerThread)
52 , m_doneProcessingDebuggerEvents(true)
53 , m_callingListeners(false)
54{
55}
56
57ScriptDebugServer::~ScriptDebugServer()
58{
59}
60
61JSC::BreakpointID ScriptDebugServer::setBreakpoint(JSC::SourceID sourceID, const ScriptBreakpoint& scriptBreakpoint, unsigned* actualLineNumber, unsigned* actualColumnNumber)
62{
63 if (!sourceID)
64 return JSC::noBreakpointID;
65
66 JSC::Breakpoint breakpoint(sourceID, scriptBreakpoint.lineNumber, scriptBreakpoint.columnNumber, scriptBreakpoint.condition, scriptBreakpoint.autoContinue);
67 JSC::BreakpointID id = Debugger::setBreakpoint(breakpoint, *actualLineNumber, *actualColumnNumber);
68 if (id != JSC::noBreakpointID && !scriptBreakpoint.actions.isEmpty()) {
69#ifndef NDEBUG
70 BreakpointIDToActionsMap::iterator it = m_breakpointIDToActions.find(id);
71 ASSERT(it == m_breakpointIDToActions.end());
72#endif
73 const BreakpointActions& actions = scriptBreakpoint.actions;
74 m_breakpointIDToActions.set(id, actions);
75 }
76 return id;
77}
78
79void ScriptDebugServer::removeBreakpoint(JSC::BreakpointID id)
80{
81 ASSERT(id != JSC::noBreakpointID);
82 BreakpointIDToActionsMap::iterator it = m_breakpointIDToActions.find(id);
83 if (it != m_breakpointIDToActions.end())
84 m_breakpointIDToActions.remove(it);
85
86 Debugger::removeBreakpoint(id);
87}
88
89bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& breakpointAction)
90{
91 DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame();
92
93 switch (breakpointAction.type) {
94 case ScriptBreakpointActionTypeLog: {
95 dispatchBreakpointActionLog(debuggerCallFrame->exec(), breakpointAction.data);
96 break;
97 }
98 case ScriptBreakpointActionTypeEvaluate: {
99 JSValue exception;
100 debuggerCallFrame->evaluate(breakpointAction.data, exception);
101 if (exception)
102 reportException(debuggerCallFrame->exec(), exception);
103 break;
104 }
105 case ScriptBreakpointActionTypeSound:
106 dispatchBreakpointActionSound(debuggerCallFrame->exec(), breakpointAction.identifier);
107 break;
108 case ScriptBreakpointActionTypeProbe: {
109 JSValue exception;
110 JSValue result = debuggerCallFrame->evaluate(breakpointAction.data, exception);
111 if (exception)
112 reportException(debuggerCallFrame->exec(), exception);
113
114 JSC::ExecState* state = debuggerCallFrame->scope()->globalObject()->globalExec();
115 Deprecated::ScriptValue wrappedResult = Deprecated::ScriptValue(state->vm(), exception ? exception : result);
116 dispatchBreakpointActionProbe(state, breakpointAction, wrappedResult);
117 break;
118 }
119 default:
120 ASSERT_NOT_REACHED();
121 }
122
123 return true;
124}
125
126void ScriptDebugServer::clearBreakpoints()
127{
128 Debugger::clearBreakpoints();
129 m_breakpointIDToActions.clear();
130}
131
132void ScriptDebugServer::dispatchDidPause(ScriptDebugListener* listener)
133{
134 ASSERT(isPaused());
135 DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame();
136 JSGlobalObject* globalObject = debuggerCallFrame->scope()->globalObject();
137 JSC::ExecState* state = globalObject->globalExec();
138 RefPtr<JavaScriptCallFrame> javaScriptCallFrame = JavaScriptCallFrame::create(debuggerCallFrame);
139 JSValue jsCallFrame = toJS(state, globalObject, javaScriptCallFrame.get());
140 listener->didPause(state, Deprecated::ScriptValue(state->vm(), jsCallFrame), Deprecated::ScriptValue());
141}
142
143void ScriptDebugServer::dispatchBreakpointActionLog(ExecState* exec, const String& message)
144{
145 if (m_callingListeners)
146 return;
147
148 ListenerSet* listeners = getListenersForGlobalObject(exec->lexicalGlobalObject());
149 if (!listeners)
150 return;
151 ASSERT(!listeners->isEmpty());
152
153 TemporaryChange<bool> change(m_callingListeners, true);
154
155 Vector<ScriptDebugListener*> listenersCopy;
156 copyToVector(*listeners, listenersCopy);
157 for (auto* listener : listenersCopy)
158 listener->breakpointActionLog(exec, message);
159}
160
161void ScriptDebugServer::dispatchBreakpointActionSound(ExecState* exec, int breakpointActionIdentifier)
162{
163 if (m_callingListeners)
164 return;
165
166 ListenerSet* listeners = getListenersForGlobalObject(exec->lexicalGlobalObject());
167 if (!listeners)
168 return;
169 ASSERT(!listeners->isEmpty());
170
171 TemporaryChange<bool> change(m_callingListeners, true);
172
173 Vector<ScriptDebugListener*> listenersCopy;
174 copyToVector(*listeners, listenersCopy);
175 for (auto* listener : listenersCopy)
176 listener->breakpointActionSound(breakpointActionIdentifier);
177}
178
179void ScriptDebugServer::dispatchBreakpointActionProbe(ExecState* exec, const ScriptBreakpointAction& action, const Deprecated::ScriptValue& sample)
180{
181 if (m_callingListeners)
182 return;
183
184 ListenerSet* listeners = getListenersForGlobalObject(exec->lexicalGlobalObject());
185 if (!listeners)
186 return;
187 ASSERT(!listeners->isEmpty());
188
189 TemporaryChange<bool> change(m_callingListeners, true);
190
191 Vector<ScriptDebugListener*> listenersCopy;
192 copyToVector(*listeners, listenersCopy);
193 for (auto* listener : listenersCopy)
194 listener->breakpointActionProbe(exec, action, m_hitCount, sample);
195}
196
197void ScriptDebugServer::dispatchDidContinue(ScriptDebugListener* listener)
198{
199 listener->didContinue();
200}
201
202void ScriptDebugServer::dispatchDidParseSource(const ListenerSet& listeners, SourceProvider* sourceProvider, bool isContentScript)
203{
204 JSC::SourceID sourceID = sourceProvider->asID();
205
206 ScriptDebugListener::Script script;
207 script.url = sourceProvider->url();
208 script.source = sourceProvider->source();
209 script.startLine = sourceProvider->startPosition().m_line.zeroBasedInt();
210 script.startColumn = sourceProvider->startPosition().m_column.zeroBasedInt();
211 script.isContentScript = isContentScript;
212
213 int sourceLength = script.source.length();
214 int lineCount = 1;
215 int lastLineStart = 0;
216 for (int i = 0; i < sourceLength; ++i) {
217 if (script.source[i] == '\n') {
218 lineCount += 1;
219 lastLineStart = i + 1;
220 }
221 }
222
223 script.endLine = script.startLine + lineCount - 1;
224 if (lineCount == 1)
225 script.endColumn = script.startColumn + sourceLength;
226 else
227 script.endColumn = sourceLength - lastLineStart;
228
229 Vector<ScriptDebugListener*> copy;
230 copyToVector(listeners, copy);
231 for (size_t i = 0; i < copy.size(); ++i)
232 copy[i]->didParseSource(sourceID, script);
233}
234
235void ScriptDebugServer::dispatchFailedToParseSource(const ListenerSet& listeners, SourceProvider* sourceProvider, int errorLine, const String& errorMessage)
236{
237 String url = sourceProvider->url();
238 const String& data = sourceProvider->source();
239 int firstLine = sourceProvider->startPosition().m_line.oneBasedInt();
240
241 Vector<ScriptDebugListener*> copy;
242 copyToVector(listeners, copy);
243 for (size_t i = 0; i < copy.size(); ++i)
244 copy[i]->failedToParseSource(url, data, firstLine, errorLine, errorMessage);
245}
246
247void ScriptDebugServer::sourceParsed(ExecState* exec, SourceProvider* sourceProvider, int errorLine, const String& errorMessage)
248{
249 if (m_callingListeners)
250 return;
251
252 ListenerSet* listeners = getListenersForGlobalObject(exec->lexicalGlobalObject());
253 if (!listeners)
254 return;
255 ASSERT(!listeners->isEmpty());
256
257 TemporaryChange<bool> change(m_callingListeners, true);
258
259 bool isError = errorLine != -1;
260 if (isError)
261 dispatchFailedToParseSource(*listeners, sourceProvider, errorLine, errorMessage);
262 else
263 dispatchDidParseSource(*listeners, sourceProvider, isContentScript(exec));
264}
265
266void ScriptDebugServer::dispatchFunctionToListeners(const ListenerSet& listeners, JavaScriptExecutionCallback callback)
267{
268 Vector<ScriptDebugListener*> copy;
269 copyToVector(listeners, copy);
270 for (size_t i = 0; i < copy.size(); ++i)
271 (this->*callback)(copy[i]);
272}
273
274void ScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallback callback, JSGlobalObject* globalObject)
275{
276 if (m_callingListeners)
277 return;
278
279 TemporaryChange<bool> change(m_callingListeners, true);
280
281 if (ListenerSet* listeners = getListenersForGlobalObject(globalObject)) {
282 if (!listeners->isEmpty())
283 dispatchFunctionToListeners(*listeners, callback);
284 }
285}
286
287void ScriptDebugServer::notifyDoneProcessingDebuggerEvents()
288{
289 m_doneProcessingDebuggerEvents = true;
290}
291
292bool ScriptDebugServer::needPauseHandling(JSGlobalObject* globalObject)
293{
294 return !!getListenersForGlobalObject(globalObject);
295}
296
297void ScriptDebugServer::handleBreakpointHit(const JSC::Breakpoint& breakpoint)
298{
299 m_hitCount++;
300 BreakpointIDToActionsMap::iterator it = m_breakpointIDToActions.find(breakpoint.id);
301 if (it != m_breakpointIDToActions.end()) {
302 BreakpointActions& actions = it->value;
303 for (size_t i = 0; i < actions.size(); ++i) {
304 if (!evaluateBreakpointAction(actions[i]))
305 return;
306 }
307 }
308}
309
310void ScriptDebugServer::handleExceptionInBreakpointCondition(JSC::ExecState* exec, JSC::JSValue exception) const
311{
312 reportException(exec, exception);
313}
314
315void ScriptDebugServer::handlePause(Debugger::ReasonForPause, JSGlobalObject* vmEntryGlobalObject)
316{
317 dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidPause, vmEntryGlobalObject);
318 didPause(vmEntryGlobalObject);
319
320 m_doneProcessingDebuggerEvents = false;
321 runEventLoopWhilePaused();
322
323 didContinue(vmEntryGlobalObject);
324 dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidContinue, vmEntryGlobalObject);
325}
326
327const BreakpointActions& ScriptDebugServer::getActionsForBreakpoint(JSC::BreakpointID breakpointID)
328{
329 ASSERT(breakpointID != JSC::noBreakpointID);
330
331 if (m_breakpointIDToActions.contains(breakpointID))
332 return m_breakpointIDToActions.find(breakpointID)->value;
333
334 static NeverDestroyed<BreakpointActions> emptyActionVector = BreakpointActions();
335 return emptyActionVector;
336}
337
338} // namespace Inspector
339
340#endif // ENABLE(INSPECTOR)