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.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
32 #include "ScriptDebugServer.h"
36 #include "DebuggerCallFrame.h"
37 #include "JSJavaScriptCallFrame.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>
50 ScriptDebugServer::ScriptDebugServer(bool isInWorkerThread
)
51 : Debugger(isInWorkerThread
)
52 , m_doneProcessingDebuggerEvents(true)
53 , m_callingListeners(false)
57 ScriptDebugServer::~ScriptDebugServer()
61 JSC::BreakpointID
ScriptDebugServer::setBreakpoint(JSC::SourceID sourceID
, const ScriptBreakpoint
& scriptBreakpoint
, unsigned* actualLineNumber
, unsigned* actualColumnNumber
)
64 return JSC::noBreakpointID
;
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()) {
70 BreakpointIDToActionsMap::iterator it
= m_breakpointIDToActions
.find(id
);
71 ASSERT(it
== m_breakpointIDToActions
.end());
73 const BreakpointActions
& actions
= scriptBreakpoint
.actions
;
74 m_breakpointIDToActions
.set(id
, actions
);
79 void ScriptDebugServer::removeBreakpoint(JSC::BreakpointID id
)
81 ASSERT(id
!= JSC::noBreakpointID
);
82 BreakpointIDToActionsMap::iterator it
= m_breakpointIDToActions
.find(id
);
83 if (it
!= m_breakpointIDToActions
.end())
84 m_breakpointIDToActions
.remove(it
);
86 Debugger::removeBreakpoint(id
);
89 bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction
& breakpointAction
)
91 DebuggerCallFrame
* debuggerCallFrame
= currentDebuggerCallFrame();
93 switch (breakpointAction
.type
) {
94 case ScriptBreakpointActionTypeLog
: {
95 dispatchBreakpointActionLog(debuggerCallFrame
->exec(), breakpointAction
.data
);
98 case ScriptBreakpointActionTypeEvaluate
: {
100 debuggerCallFrame
->evaluate(breakpointAction
.data
, exception
);
102 reportException(debuggerCallFrame
->exec(), exception
);
105 case ScriptBreakpointActionTypeSound
:
106 dispatchBreakpointActionSound(debuggerCallFrame
->exec(), breakpointAction
.identifier
);
108 case ScriptBreakpointActionTypeProbe
: {
110 JSValue result
= debuggerCallFrame
->evaluate(breakpointAction
.data
, exception
);
112 reportException(debuggerCallFrame
->exec(), exception
);
114 JSC::ExecState
* state
= debuggerCallFrame
->scope()->globalObject()->globalExec();
115 Deprecated::ScriptValue wrappedResult
= Deprecated::ScriptValue(state
->vm(), exception
? exception
: result
);
116 dispatchBreakpointActionProbe(state
, breakpointAction
, wrappedResult
);
120 ASSERT_NOT_REACHED();
126 void ScriptDebugServer::clearBreakpoints()
128 Debugger::clearBreakpoints();
129 m_breakpointIDToActions
.clear();
132 void ScriptDebugServer::dispatchDidPause(ScriptDebugListener
* listener
)
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());
143 void ScriptDebugServer::dispatchBreakpointActionLog(ExecState
* exec
, const String
& message
)
145 if (m_callingListeners
)
148 ListenerSet
* listeners
= getListenersForGlobalObject(exec
->lexicalGlobalObject());
151 ASSERT(!listeners
->isEmpty());
153 TemporaryChange
<bool> change(m_callingListeners
, true);
155 Vector
<ScriptDebugListener
*> listenersCopy
;
156 copyToVector(*listeners
, listenersCopy
);
157 for (auto* listener
: listenersCopy
)
158 listener
->breakpointActionLog(exec
, message
);
161 void ScriptDebugServer::dispatchBreakpointActionSound(ExecState
* exec
, int breakpointActionIdentifier
)
163 if (m_callingListeners
)
166 ListenerSet
* listeners
= getListenersForGlobalObject(exec
->lexicalGlobalObject());
169 ASSERT(!listeners
->isEmpty());
171 TemporaryChange
<bool> change(m_callingListeners
, true);
173 Vector
<ScriptDebugListener
*> listenersCopy
;
174 copyToVector(*listeners
, listenersCopy
);
175 for (auto* listener
: listenersCopy
)
176 listener
->breakpointActionSound(breakpointActionIdentifier
);
179 void ScriptDebugServer::dispatchBreakpointActionProbe(ExecState
* exec
, const ScriptBreakpointAction
& action
, const Deprecated::ScriptValue
& sample
)
181 if (m_callingListeners
)
184 ListenerSet
* listeners
= getListenersForGlobalObject(exec
->lexicalGlobalObject());
187 ASSERT(!listeners
->isEmpty());
189 TemporaryChange
<bool> change(m_callingListeners
, true);
191 Vector
<ScriptDebugListener
*> listenersCopy
;
192 copyToVector(*listeners
, listenersCopy
);
193 for (auto* listener
: listenersCopy
)
194 listener
->breakpointActionProbe(exec
, action
, m_hitCount
, sample
);
197 void ScriptDebugServer::dispatchDidContinue(ScriptDebugListener
* listener
)
199 listener
->didContinue();
202 void ScriptDebugServer::dispatchDidParseSource(const ListenerSet
& listeners
, SourceProvider
* sourceProvider
, bool isContentScript
)
204 JSC::SourceID sourceID
= sourceProvider
->asID();
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
;
213 int sourceLength
= script
.source
.length();
215 int lastLineStart
= 0;
216 for (int i
= 0; i
< sourceLength
; ++i
) {
217 if (script
.source
[i
] == '\n') {
219 lastLineStart
= i
+ 1;
223 script
.endLine
= script
.startLine
+ lineCount
- 1;
225 script
.endColumn
= script
.startColumn
+ sourceLength
;
227 script
.endColumn
= sourceLength
- lastLineStart
;
229 Vector
<ScriptDebugListener
*> copy
;
230 copyToVector(listeners
, copy
);
231 for (size_t i
= 0; i
< copy
.size(); ++i
)
232 copy
[i
]->didParseSource(sourceID
, script
);
235 void ScriptDebugServer::dispatchFailedToParseSource(const ListenerSet
& listeners
, SourceProvider
* sourceProvider
, int errorLine
, const String
& errorMessage
)
237 String url
= sourceProvider
->url();
238 const String
& data
= sourceProvider
->source();
239 int firstLine
= sourceProvider
->startPosition().m_line
.oneBasedInt();
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
);
247 void ScriptDebugServer::sourceParsed(ExecState
* exec
, SourceProvider
* sourceProvider
, int errorLine
, const String
& errorMessage
)
249 if (m_callingListeners
)
252 ListenerSet
* listeners
= getListenersForGlobalObject(exec
->lexicalGlobalObject());
255 ASSERT(!listeners
->isEmpty());
257 TemporaryChange
<bool> change(m_callingListeners
, true);
259 bool isError
= errorLine
!= -1;
261 dispatchFailedToParseSource(*listeners
, sourceProvider
, errorLine
, errorMessage
);
263 dispatchDidParseSource(*listeners
, sourceProvider
, isContentScript(exec
));
266 void ScriptDebugServer::dispatchFunctionToListeners(const ListenerSet
& listeners
, JavaScriptExecutionCallback callback
)
268 Vector
<ScriptDebugListener
*> copy
;
269 copyToVector(listeners
, copy
);
270 for (size_t i
= 0; i
< copy
.size(); ++i
)
271 (this->*callback
)(copy
[i
]);
274 void ScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallback callback
, JSGlobalObject
* globalObject
)
276 if (m_callingListeners
)
279 TemporaryChange
<bool> change(m_callingListeners
, true);
281 if (ListenerSet
* listeners
= getListenersForGlobalObject(globalObject
)) {
282 if (!listeners
->isEmpty())
283 dispatchFunctionToListeners(*listeners
, callback
);
287 void ScriptDebugServer::notifyDoneProcessingDebuggerEvents()
289 m_doneProcessingDebuggerEvents
= true;
292 bool ScriptDebugServer::needPauseHandling(JSGlobalObject
* globalObject
)
294 return !!getListenersForGlobalObject(globalObject
);
297 void ScriptDebugServer::handleBreakpointHit(const JSC::Breakpoint
& breakpoint
)
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
]))
310 void ScriptDebugServer::handleExceptionInBreakpointCondition(JSC::ExecState
* exec
, JSC::JSValue exception
) const
312 reportException(exec
, exception
);
315 void ScriptDebugServer::handlePause(Debugger::ReasonForPause
, JSGlobalObject
* vmEntryGlobalObject
)
317 dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidPause
, vmEntryGlobalObject
);
318 didPause(vmEntryGlobalObject
);
320 m_doneProcessingDebuggerEvents
= false;
321 runEventLoopWhilePaused();
323 didContinue(vmEntryGlobalObject
);
324 dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidContinue
, vmEntryGlobalObject
);
327 const BreakpointActions
& ScriptDebugServer::getActionsForBreakpoint(JSC::BreakpointID breakpointID
)
329 ASSERT(breakpointID
!= JSC::noBreakpointID
);
331 if (m_breakpointIDToActions
.contains(breakpointID
))
332 return m_breakpointIDToActions
.find(breakpointID
)->value
;
334 static NeverDestroyed
<BreakpointActions
> emptyActionVector
= BreakpointActions();
335 return emptyActionVector
;
338 } // namespace Inspector
340 #endif // ENABLE(INSPECTOR)