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"
34 #include "DebuggerCallFrame.h"
35 #include "DebuggerScope.h"
36 #include "Exception.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
)
55 ScriptDebugServer::~ScriptDebugServer()
59 JSC::BreakpointID
ScriptDebugServer::setBreakpoint(JSC::SourceID sourceID
, const ScriptBreakpoint
& scriptBreakpoint
, unsigned* actualLineNumber
, unsigned* actualColumnNumber
)
62 return JSC::noBreakpointID
;
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()) {
68 BreakpointIDToActionsMap::iterator it
= m_breakpointIDToActions
.find(id
);
69 ASSERT(it
== m_breakpointIDToActions
.end());
71 const BreakpointActions
& actions
= scriptBreakpoint
.actions
;
72 m_breakpointIDToActions
.set(id
, actions
);
77 void ScriptDebugServer::removeBreakpoint(JSC::BreakpointID id
)
79 ASSERT(id
!= JSC::noBreakpointID
);
80 BreakpointIDToActionsMap::iterator it
= m_breakpointIDToActions
.find(id
);
81 if (it
!= m_breakpointIDToActions
.end())
82 m_breakpointIDToActions
.remove(it
);
84 Debugger::removeBreakpoint(id
);
87 bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction
& breakpointAction
)
89 DebuggerCallFrame
* debuggerCallFrame
= currentDebuggerCallFrame();
91 switch (breakpointAction
.type
) {
92 case ScriptBreakpointActionTypeLog
: {
93 dispatchBreakpointActionLog(debuggerCallFrame
->exec(), breakpointAction
.data
);
96 case ScriptBreakpointActionTypeEvaluate
: {
97 NakedPtr
<Exception
> exception
;
98 debuggerCallFrame
->evaluate(breakpointAction
.data
, exception
);
100 reportException(debuggerCallFrame
->exec(), exception
);
103 case ScriptBreakpointActionTypeSound
:
104 dispatchBreakpointActionSound(debuggerCallFrame
->exec(), breakpointAction
.identifier
);
106 case ScriptBreakpointActionTypeProbe
: {
107 NakedPtr
<Exception
> exception
;
108 JSValue result
= debuggerCallFrame
->evaluate(breakpointAction
.data
, exception
);
110 reportException(debuggerCallFrame
->exec(), exception
);
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
);
118 ASSERT_NOT_REACHED();
124 void ScriptDebugServer::clearBreakpoints()
126 Debugger::clearBreakpoints();
127 m_breakpointIDToActions
.clear();
130 void ScriptDebugServer::dispatchDidPause(ScriptDebugListener
* listener
)
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());
139 listener
->didPause(state
, Deprecated::ScriptValue(state
->vm(), jsCallFrame
), exceptionOrCaughtValue(state
));
142 void ScriptDebugServer::dispatchBreakpointActionLog(ExecState
* exec
, const String
& message
)
144 if (m_callingListeners
)
147 ListenerSet
& listeners
= getListeners();
148 if (listeners
.isEmpty())
151 TemporaryChange
<bool> change(m_callingListeners
, true);
153 Vector
<ScriptDebugListener
*> listenersCopy
;
154 copyToVector(listeners
, listenersCopy
);
155 for (auto* listener
: listenersCopy
)
156 listener
->breakpointActionLog(exec
, message
);
159 void ScriptDebugServer::dispatchBreakpointActionSound(ExecState
*, int breakpointActionIdentifier
)
161 if (m_callingListeners
)
164 ListenerSet
& listeners
= getListeners();
165 if (listeners
.isEmpty())
168 TemporaryChange
<bool> change(m_callingListeners
, true);
170 Vector
<ScriptDebugListener
*> listenersCopy
;
171 copyToVector(listeners
, listenersCopy
);
172 for (auto* listener
: listenersCopy
)
173 listener
->breakpointActionSound(breakpointActionIdentifier
);
176 void ScriptDebugServer::dispatchBreakpointActionProbe(ExecState
* exec
, const ScriptBreakpointAction
& action
, const Deprecated::ScriptValue
& sampleValue
)
178 if (m_callingListeners
)
181 ListenerSet
& listeners
= getListeners();
182 if (listeners
.isEmpty())
185 TemporaryChange
<bool> change(m_callingListeners
, true);
187 unsigned sampleId
= m_nextProbeSampleId
++;
189 Vector
<ScriptDebugListener
*> listenersCopy
;
190 copyToVector(listeners
, listenersCopy
);
191 for (auto* listener
: listenersCopy
)
192 listener
->breakpointActionProbe(exec
, action
, m_currentProbeBatchId
, sampleId
, sampleValue
);
195 void ScriptDebugServer::dispatchDidContinue(ScriptDebugListener
* listener
)
197 listener
->didContinue();
200 void ScriptDebugServer::dispatchDidParseSource(const ListenerSet
& listeners
, SourceProvider
* sourceProvider
, bool isContentScript
)
202 JSC::SourceID sourceID
= sourceProvider
->asID();
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
;
211 int sourceLength
= script
.source
.length();
213 int lastLineStart
= 0;
214 for (int i
= 0; i
< sourceLength
; ++i
) {
215 if (script
.source
[i
] == '\n') {
217 lastLineStart
= i
+ 1;
221 script
.endLine
= script
.startLine
+ lineCount
- 1;
223 script
.endColumn
= script
.startColumn
+ sourceLength
;
225 script
.endColumn
= sourceLength
- lastLineStart
;
227 Vector
<ScriptDebugListener
*> copy
;
228 copyToVector(listeners
, copy
);
229 for (size_t i
= 0; i
< copy
.size(); ++i
)
230 copy
[i
]->didParseSource(sourceID
, script
);
233 void ScriptDebugServer::dispatchFailedToParseSource(const ListenerSet
& listeners
, SourceProvider
* sourceProvider
, int errorLine
, const String
& errorMessage
)
235 String url
= sourceProvider
->url();
236 const String
& data
= sourceProvider
->source();
237 int firstLine
= sourceProvider
->startPosition().m_line
.oneBasedInt();
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
);
245 void ScriptDebugServer::sourceParsed(ExecState
* exec
, SourceProvider
* sourceProvider
, int errorLine
, const String
& errorMessage
)
247 if (m_callingListeners
)
250 ListenerSet
& listeners
= getListeners();
251 if (listeners
.isEmpty())
254 TemporaryChange
<bool> change(m_callingListeners
, true);
256 bool isError
= errorLine
!= -1;
258 dispatchFailedToParseSource(listeners
, sourceProvider
, errorLine
, errorMessage
);
260 dispatchDidParseSource(listeners
, sourceProvider
, isContentScript(exec
));
263 void ScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallback callback
)
265 if (m_callingListeners
)
268 TemporaryChange
<bool> change(m_callingListeners
, true);
270 ListenerSet
& listeners
= getListeners();
271 if (!listeners
.isEmpty())
272 dispatchFunctionToListeners(listeners
, callback
);
275 void ScriptDebugServer::dispatchFunctionToListeners(const ListenerSet
& listeners
, JavaScriptExecutionCallback callback
)
277 Vector
<ScriptDebugListener
*> copy
;
278 copyToVector(listeners
, copy
);
279 for (size_t i
= 0; i
< copy
.size(); ++i
)
280 (this->*callback
)(copy
[i
]);
283 void ScriptDebugServer::notifyDoneProcessingDebuggerEvents()
285 m_doneProcessingDebuggerEvents
= true;
288 void ScriptDebugServer::handleBreakpointHit(JSC::JSGlobalObject
* globalObject
, const JSC::Breakpoint
& breakpoint
)
290 ASSERT(isAttached(globalObject
));
292 m_currentProbeBatchId
++;
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
]))
300 if (!isAttached(globalObject
))
306 void ScriptDebugServer::handleExceptionInBreakpointCondition(JSC::ExecState
* exec
, JSC::Exception
* exception
) const
308 reportException(exec
, exception
);
311 void ScriptDebugServer::handlePause(JSGlobalObject
* vmEntryGlobalObject
, Debugger::ReasonForPause
)
313 dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidPause
);
314 didPause(vmEntryGlobalObject
);
316 m_doneProcessingDebuggerEvents
= false;
317 runEventLoopWhilePaused();
319 didContinue(vmEntryGlobalObject
);
320 dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidContinue
);
323 const BreakpointActions
& ScriptDebugServer::getActionsForBreakpoint(JSC::BreakpointID breakpointID
)
325 ASSERT(breakpointID
!= JSC::noBreakpointID
);
327 if (m_breakpointIDToActions
.contains(breakpointID
))
328 return m_breakpointIDToActions
.find(breakpointID
)->value
;
330 static NeverDestroyed
<BreakpointActions
> emptyActionVector
= BreakpointActions();
331 return emptyActionVector
;
334 Deprecated::ScriptValue
ScriptDebugServer::exceptionOrCaughtValue(JSC::ExecState
* state
)
336 if (reasonForPause() == PausedForException
)
337 return Deprecated::ScriptValue(state
->vm(), currentException());
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();
347 return Deprecated::ScriptValue();
350 } // namespace Inspector