2 * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
3 * Copyright (C) 2011 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include "InspectorRuntimeAgent.h"
35 #include "Completion.h"
36 #include "HeapIterationScope.h"
37 #include "InjectedScript.h"
38 #include "InjectedScriptManager.h"
39 #include "InspectorValues.h"
41 #include "ParserError.h"
42 #include "ScriptDebugServer.h"
43 #include "SourceCode.h"
44 #include "TypeProfiler.h"
45 #include "TypeProfilerLog.h"
46 #include "VMEntryScope.h"
47 #include <wtf/CurrentTime.h>
53 static bool asBool(const bool* const b
)
55 return b
? *b
: false;
58 InspectorRuntimeAgent::InspectorRuntimeAgent(InjectedScriptManager
* injectedScriptManager
)
59 : InspectorAgentBase(ASCIILiteral("Runtime"))
60 , m_injectedScriptManager(injectedScriptManager
)
61 , m_scriptDebugServer(nullptr)
63 , m_isTypeProfilingEnabled(false)
67 InspectorRuntimeAgent::~InspectorRuntimeAgent()
71 static ScriptDebugServer::PauseOnExceptionsState
setPauseOnExceptionsState(ScriptDebugServer
* scriptDebugServer
, ScriptDebugServer::PauseOnExceptionsState newState
)
73 ASSERT(scriptDebugServer
);
74 ScriptDebugServer::PauseOnExceptionsState presentState
= scriptDebugServer
->pauseOnExceptionsState();
75 if (presentState
!= newState
)
76 scriptDebugServer
->setPauseOnExceptionsState(newState
);
80 static Ref
<Inspector::Protocol::Runtime::ErrorRange
> buildErrorRangeObject(const JSTokenLocation
& tokenLocation
)
82 return Inspector::Protocol::Runtime::ErrorRange::create()
83 .setStartOffset(tokenLocation
.startOffset
)
84 .setEndOffset(tokenLocation
.endOffset
)
88 void InspectorRuntimeAgent::parse(ErrorString
&, const String
& expression
, Inspector::Protocol::Runtime::SyntaxErrorType
* result
, Inspector::Protocol::OptOutput
<String
>* message
, RefPtr
<Inspector::Protocol::Runtime::ErrorRange
>& range
)
91 JSLockHolder
lock(vm
);
94 checkSyntax(vm
, JSC::makeSource(expression
), error
);
96 switch (error
.syntaxErrorType()) {
97 case ParserError::SyntaxErrorNone
:
98 *result
= Inspector::Protocol::Runtime::SyntaxErrorType::None
;
100 case ParserError::SyntaxErrorIrrecoverable
:
101 *result
= Inspector::Protocol::Runtime::SyntaxErrorType::Irrecoverable
;
103 case ParserError::SyntaxErrorUnterminatedLiteral
:
104 *result
= Inspector::Protocol::Runtime::SyntaxErrorType::UnterminatedLiteral
;
106 case ParserError::SyntaxErrorRecoverable
:
107 *result
= Inspector::Protocol::Runtime::SyntaxErrorType::Recoverable
;
111 if (error
.syntaxErrorType() != ParserError::SyntaxErrorNone
) {
112 *message
= error
.message();
113 range
= buildErrorRangeObject(error
.token().m_location
);
117 void InspectorRuntimeAgent::evaluate(ErrorString
& errorString
, const String
& expression
, const String
* const objectGroup
, const bool* const includeCommandLineAPI
, const bool* const doNotPauseOnExceptionsAndMuteConsole
, const int* executionContextId
, const bool* const returnByValue
, const bool* generatePreview
, const bool* saveResult
, RefPtr
<Inspector::Protocol::Runtime::RemoteObject
>& result
, Inspector::Protocol::OptOutput
<bool>* wasThrown
, Inspector::Protocol::OptOutput
<int>* savedResultIndex
)
119 InjectedScript injectedScript
= injectedScriptForEval(errorString
, executionContextId
);
120 if (injectedScript
.hasNoValue())
123 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState
= ScriptDebugServer::DontPauseOnExceptions
;
124 if (asBool(doNotPauseOnExceptionsAndMuteConsole
))
125 previousPauseOnExceptionsState
= setPauseOnExceptionsState(m_scriptDebugServer
, ScriptDebugServer::DontPauseOnExceptions
);
126 if (asBool(doNotPauseOnExceptionsAndMuteConsole
))
129 injectedScript
.evaluate(errorString
, expression
, objectGroup
? *objectGroup
: String(), asBool(includeCommandLineAPI
), asBool(returnByValue
), asBool(generatePreview
), asBool(saveResult
), &result
, wasThrown
, savedResultIndex
);
131 if (asBool(doNotPauseOnExceptionsAndMuteConsole
)) {
133 setPauseOnExceptionsState(m_scriptDebugServer
, previousPauseOnExceptionsState
);
137 void InspectorRuntimeAgent::callFunctionOn(ErrorString
& errorString
, const String
& objectId
, const String
& expression
, const InspectorArray
* optionalArguments
, const bool* const doNotPauseOnExceptionsAndMuteConsole
, const bool* const returnByValue
, const bool* generatePreview
, RefPtr
<Inspector::Protocol::Runtime::RemoteObject
>& result
, Inspector::Protocol::OptOutput
<bool>* wasThrown
)
139 InjectedScript injectedScript
= m_injectedScriptManager
->injectedScriptForObjectId(objectId
);
140 if (injectedScript
.hasNoValue()) {
141 errorString
= ASCIILiteral("Inspected frame has gone");
146 if (optionalArguments
)
147 arguments
= optionalArguments
->toJSONString();
149 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState
= ScriptDebugServer::DontPauseOnExceptions
;
150 if (asBool(doNotPauseOnExceptionsAndMuteConsole
))
151 previousPauseOnExceptionsState
= setPauseOnExceptionsState(m_scriptDebugServer
, ScriptDebugServer::DontPauseOnExceptions
);
152 if (asBool(doNotPauseOnExceptionsAndMuteConsole
))
155 injectedScript
.callFunctionOn(errorString
, objectId
, expression
, arguments
, asBool(returnByValue
), asBool(generatePreview
), &result
, wasThrown
);
157 if (asBool(doNotPauseOnExceptionsAndMuteConsole
)) {
159 setPauseOnExceptionsState(m_scriptDebugServer
, previousPauseOnExceptionsState
);
163 void InspectorRuntimeAgent::getProperties(ErrorString
& errorString
, const String
& objectId
, const bool* const ownProperties
, const bool* const generatePreview
, RefPtr
<Inspector::Protocol::Array
<Inspector::Protocol::Runtime::PropertyDescriptor
>>& result
, RefPtr
<Inspector::Protocol::Array
<Inspector::Protocol::Runtime::InternalPropertyDescriptor
>>& internalProperties
)
165 InjectedScript injectedScript
= m_injectedScriptManager
->injectedScriptForObjectId(objectId
);
166 if (injectedScript
.hasNoValue()) {
167 errorString
= ASCIILiteral("Inspected frame has gone");
171 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState
= setPauseOnExceptionsState(m_scriptDebugServer
, ScriptDebugServer::DontPauseOnExceptions
);
174 injectedScript
.getProperties(errorString
, objectId
, asBool(ownProperties
), asBool(generatePreview
), &result
);
175 injectedScript
.getInternalProperties(errorString
, objectId
, asBool(generatePreview
), &internalProperties
);
178 setPauseOnExceptionsState(m_scriptDebugServer
, previousPauseOnExceptionsState
);
181 void InspectorRuntimeAgent::getDisplayableProperties(ErrorString
& errorString
, const String
& objectId
, const bool* const generatePreview
, RefPtr
<Inspector::Protocol::Array
<Inspector::Protocol::Runtime::PropertyDescriptor
>>& result
, RefPtr
<Inspector::Protocol::Array
<Inspector::Protocol::Runtime::InternalPropertyDescriptor
>>& internalProperties
)
183 InjectedScript injectedScript
= m_injectedScriptManager
->injectedScriptForObjectId(objectId
);
184 if (injectedScript
.hasNoValue()) {
185 errorString
= ASCIILiteral("Inspected frame has gone");
189 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState
= setPauseOnExceptionsState(m_scriptDebugServer
, ScriptDebugServer::DontPauseOnExceptions
);
192 injectedScript
.getDisplayableProperties(errorString
, objectId
, asBool(generatePreview
), &result
);
193 injectedScript
.getInternalProperties(errorString
, objectId
, asBool(generatePreview
), &internalProperties
);
196 setPauseOnExceptionsState(m_scriptDebugServer
, previousPauseOnExceptionsState
);
199 void InspectorRuntimeAgent::getCollectionEntries(ErrorString
& errorString
, const String
& objectId
, const String
* objectGroup
, const int* startIndex
, const int* numberToFetch
, RefPtr
<Inspector::Protocol::Array
<Inspector::Protocol::Runtime::CollectionEntry
>>& entries
)
201 InjectedScript injectedScript
= m_injectedScriptManager
->injectedScriptForObjectId(objectId
);
202 if (injectedScript
.hasNoValue()) {
203 errorString
= ASCIILiteral("Inspected frame has gone");
207 int start
= startIndex
&& *startIndex
>= 0 ? *startIndex
: 0;
208 int fetch
= numberToFetch
&& *numberToFetch
>= 0 ? *numberToFetch
: 0;
210 injectedScript
.getCollectionEntries(errorString
, objectId
, objectGroup
? *objectGroup
: String(), start
, fetch
, &entries
);
213 void InspectorRuntimeAgent::saveResult(ErrorString
& errorString
, const Inspector::InspectorObject
& callArgument
, const int* executionContextId
, Inspector::Protocol::OptOutput
<int>* savedResultIndex
)
215 InjectedScript injectedScript
;
218 if (callArgument
.getString(ASCIILiteral("objectId"), objectId
)) {
219 injectedScript
= m_injectedScriptManager
->injectedScriptForObjectId(objectId
);
220 if (injectedScript
.hasNoValue()) {
221 errorString
= ASCIILiteral("Inspected frame has gone");
225 injectedScript
= injectedScriptForEval(errorString
, executionContextId
);
226 if (injectedScript
.hasNoValue())
230 injectedScript
.saveResult(errorString
, callArgument
.toJSONString(), savedResultIndex
);
233 void InspectorRuntimeAgent::releaseObject(ErrorString
&, const String
& objectId
)
235 InjectedScript injectedScript
= m_injectedScriptManager
->injectedScriptForObjectId(objectId
);
236 if (!injectedScript
.hasNoValue())
237 injectedScript
.releaseObject(objectId
);
240 void InspectorRuntimeAgent::releaseObjectGroup(ErrorString
&, const String
& objectGroup
)
242 m_injectedScriptManager
->releaseObjectGroup(objectGroup
);
245 void InspectorRuntimeAgent::run(ErrorString
&)
247 // FIXME: <https://webkit.org/b/127634> Web Inspector: support debugging web workers
250 void InspectorRuntimeAgent::getRuntimeTypesForVariablesAtOffsets(ErrorString
& errorString
, const Inspector::InspectorArray
& locations
, RefPtr
<Inspector::Protocol::Array
<Inspector::Protocol::Runtime::TypeDescription
>>& typeDescriptions
)
252 static const bool verbose
= false;
254 typeDescriptions
= Inspector::Protocol::Array
<Inspector::Protocol::Runtime::TypeDescription
>::create();
255 if (!vm
.typeProfiler()) {
256 errorString
= ASCIILiteral("The VM does not currently have Type Information.");
260 double start
= currentTimeMS();
261 vm
.typeProfilerLog()->processLogEntries(ASCIILiteral("User Query"));
263 for (size_t i
= 0; i
< locations
.length(); i
++) {
264 RefPtr
<Inspector::InspectorValue
> value
= locations
.get(i
);
265 RefPtr
<InspectorObject
> location
;
266 if (!value
->asObject(location
)) {
267 errorString
= ASCIILiteral("Array of TypeLocation objects has an object that does not have type of TypeLocation.");
272 String sourceIDAsString
;
274 location
->getInteger(ASCIILiteral("typeInformationDescriptor"), descriptor
);
275 location
->getString(ASCIILiteral("sourceID"), sourceIDAsString
);
276 location
->getInteger(ASCIILiteral("divot"), divot
);
279 TypeLocation
* typeLocation
= vm
.typeProfiler()->findLocation(divot
, sourceIDAsString
.toIntPtrStrict(&okay
), static_cast<TypeProfilerSearchDescriptor
>(descriptor
), vm
);
282 RefPtr
<TypeSet
> typeSet
;
284 if (typeLocation
->m_globalTypeSet
&& typeLocation
->m_globalVariableID
!= TypeProfilerNoGlobalIDExists
)
285 typeSet
= typeLocation
->m_globalTypeSet
;
287 typeSet
= typeLocation
->m_instructionTypeSet
;
290 bool isValid
= typeLocation
&& typeSet
&& !typeSet
->isEmpty();
291 auto description
= Inspector::Protocol::Runtime::TypeDescription::create()
296 description
->setLeastCommonAncestor(typeSet
->leastCommonAncestor());
297 description
->setStructures(typeSet
->allStructureRepresentations());
298 description
->setTypeSet(typeSet
->inspectorTypeSet());
299 description
->setIsTruncated(typeSet
->isOverflown());
302 typeDescriptions
->addItem(WTF::move(description
));
305 double end
= currentTimeMS();
307 dataLogF("Inspector::getRuntimeTypesForVariablesAtOffsets took %lfms\n", end
- start
);
310 class TypeRecompiler
: public MarkedBlock::VoidFunctor
{
312 inline void visit(JSCell
* cell
)
314 if (!cell
->inherits(FunctionExecutable::info()))
317 FunctionExecutable
* executable
= jsCast
<FunctionExecutable
*>(cell
);
318 executable
->clearCode();
319 executable
->clearUnlinkedCodeForRecompilation();
321 inline IterationStatus
operator()(JSCell
* cell
)
324 return IterationStatus::Continue
;
328 static void recompileAllJSFunctionsForTypeProfiling(VM
& vm
, bool shouldEnableTypeProfiling
)
330 bool shouldRecompileFromTypeProfiler
= (shouldEnableTypeProfiling
? vm
.enableTypeProfiler() : vm
.disableTypeProfiler());
331 bool shouldRecompileFromControlFlowProfiler
= (shouldEnableTypeProfiling
? vm
.enableControlFlowProfiler() : vm
.disableControlFlowProfiler());
332 bool needsToRecompile
= shouldRecompileFromTypeProfiler
|| shouldRecompileFromControlFlowProfiler
;
334 if (needsToRecompile
) {
335 vm
.prepareToDiscardCode();
336 TypeRecompiler recompiler
;
337 HeapIterationScope
iterationScope(vm
.heap
);
338 vm
.heap
.objectSpace().forEachLiveCell(iterationScope
, recompiler
);
342 void InspectorRuntimeAgent::willDestroyFrontendAndBackend(DisconnectReason reason
)
344 if (reason
!= DisconnectReason::InspectedTargetDestroyed
&& m_isTypeProfilingEnabled
)
345 setTypeProfilerEnabledState(false);
348 void InspectorRuntimeAgent::enableTypeProfiler(ErrorString
&)
350 setTypeProfilerEnabledState(true);
353 void InspectorRuntimeAgent::disableTypeProfiler(ErrorString
&)
355 setTypeProfilerEnabledState(false);
358 void InspectorRuntimeAgent::setTypeProfilerEnabledState(bool shouldEnableTypeProfiling
)
360 if (m_isTypeProfilingEnabled
== shouldEnableTypeProfiling
)
363 m_isTypeProfilingEnabled
= shouldEnableTypeProfiling
;
367 // If JavaScript is running, it's not safe to recompile, since we'll end
368 // up throwing away code that is live on the stack.
370 vm
.entryScope
->setEntryScopeDidPopListener(this,
371 [=] (VM
& vm
, JSGlobalObject
*) {
372 recompileAllJSFunctionsForTypeProfiling(vm
, shouldEnableTypeProfiling
);
376 recompileAllJSFunctionsForTypeProfiling(vm
, shouldEnableTypeProfiling
);
379 void InspectorRuntimeAgent::getBasicBlocks(ErrorString
& errorString
, const String
& sourceIDAsString
, RefPtr
<Inspector::Protocol::Array
<Inspector::Protocol::Runtime::BasicBlock
>>& basicBlocks
)
382 if (!vm
.controlFlowProfiler()) {
383 errorString
= ASCIILiteral("The VM does not currently have a Control Flow Profiler.");
388 intptr_t sourceID
= sourceIDAsString
.toIntPtrStrict(&okay
);
390 const Vector
<BasicBlockRange
>& basicBlockRanges
= vm
.controlFlowProfiler()->getBasicBlocksForSourceID(sourceID
, vm
);
391 basicBlocks
= Inspector::Protocol::Array
<Inspector::Protocol::Runtime::BasicBlock
>::create();
392 for (const BasicBlockRange
& block
: basicBlockRanges
) {
393 Ref
<Inspector::Protocol::Runtime::BasicBlock
> location
= Inspector::Protocol::Runtime::BasicBlock::create()
394 .setStartOffset(block
.m_startOffset
)
395 .setEndOffset(block
.m_endOffset
)
396 .setHasExecuted(block
.m_hasExecuted
)
398 basicBlocks
->addItem(WTF::move(location
));
402 } // namespace Inspector