]> git.saurik.com Git - apple/javascriptcore.git/blame - inspector/agents/InspectorRuntimeAgent.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / inspector / agents / InspectorRuntimeAgent.cpp
CommitLineData
81345200
A
1/*
2 * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
3 * Copyright (C) 2011 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
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
14 * distribution.
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.
18 *
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.
30 */
31
32#include "config.h"
33#include "InspectorRuntimeAgent.h"
34
81345200 35#include "Completion.h"
ed1e77d3 36#include "HeapIterationScope.h"
81345200
A
37#include "InjectedScript.h"
38#include "InjectedScriptManager.h"
39#include "InspectorValues.h"
40#include "JSLock.h"
41#include "ParserError.h"
42#include "ScriptDebugServer.h"
43#include "SourceCode.h"
ed1e77d3
A
44#include "TypeProfiler.h"
45#include "TypeProfilerLog.h"
46#include "VMEntryScope.h"
47#include <wtf/CurrentTime.h>
81345200
A
48
49using namespace JSC;
50
51namespace Inspector {
52
53static bool asBool(const bool* const b)
54{
55 return b ? *b : false;
56}
57
58InspectorRuntimeAgent::InspectorRuntimeAgent(InjectedScriptManager* injectedScriptManager)
59 : InspectorAgentBase(ASCIILiteral("Runtime"))
60 , m_injectedScriptManager(injectedScriptManager)
61 , m_scriptDebugServer(nullptr)
62 , m_enabled(false)
ed1e77d3 63 , m_isTypeProfilingEnabled(false)
81345200
A
64{
65}
66
67InspectorRuntimeAgent::~InspectorRuntimeAgent()
68{
69}
70
71static ScriptDebugServer::PauseOnExceptionsState setPauseOnExceptionsState(ScriptDebugServer* scriptDebugServer, ScriptDebugServer::PauseOnExceptionsState newState)
72{
73 ASSERT(scriptDebugServer);
74 ScriptDebugServer::PauseOnExceptionsState presentState = scriptDebugServer->pauseOnExceptionsState();
75 if (presentState != newState)
76 scriptDebugServer->setPauseOnExceptionsState(newState);
77 return presentState;
78}
79
ed1e77d3 80static Ref<Inspector::Protocol::Runtime::ErrorRange> buildErrorRangeObject(const JSTokenLocation& tokenLocation)
81345200 81{
ed1e77d3 82 return Inspector::Protocol::Runtime::ErrorRange::create()
81345200 83 .setStartOffset(tokenLocation.startOffset)
ed1e77d3
A
84 .setEndOffset(tokenLocation.endOffset)
85 .release();
81345200
A
86}
87
ed1e77d3 88void InspectorRuntimeAgent::parse(ErrorString&, const String& expression, Inspector::Protocol::Runtime::SyntaxErrorType* result, Inspector::Protocol::OptOutput<String>* message, RefPtr<Inspector::Protocol::Runtime::ErrorRange>& range)
81345200
A
89{
90 VM& vm = globalVM();
91 JSLockHolder lock(vm);
92
93 ParserError error;
94 checkSyntax(vm, JSC::makeSource(expression), error);
95
ed1e77d3 96 switch (error.syntaxErrorType()) {
81345200 97 case ParserError::SyntaxErrorNone:
ed1e77d3 98 *result = Inspector::Protocol::Runtime::SyntaxErrorType::None;
81345200
A
99 break;
100 case ParserError::SyntaxErrorIrrecoverable:
ed1e77d3 101 *result = Inspector::Protocol::Runtime::SyntaxErrorType::Irrecoverable;
81345200
A
102 break;
103 case ParserError::SyntaxErrorUnterminatedLiteral:
ed1e77d3 104 *result = Inspector::Protocol::Runtime::SyntaxErrorType::UnterminatedLiteral;
81345200
A
105 break;
106 case ParserError::SyntaxErrorRecoverable:
ed1e77d3 107 *result = Inspector::Protocol::Runtime::SyntaxErrorType::Recoverable;
81345200
A
108 break;
109 }
110
ed1e77d3
A
111 if (error.syntaxErrorType() != ParserError::SyntaxErrorNone) {
112 *message = error.message();
113 range = buildErrorRangeObject(error.token().m_location);
81345200
A
114 }
115}
116
ed1e77d3 117void 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)
81345200
A
118{
119 InjectedScript injectedScript = injectedScriptForEval(errorString, executionContextId);
120 if (injectedScript.hasNoValue())
121 return;
122
123 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = ScriptDebugServer::DontPauseOnExceptions;
124 if (asBool(doNotPauseOnExceptionsAndMuteConsole))
125 previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions);
126 if (asBool(doNotPauseOnExceptionsAndMuteConsole))
127 muteConsole();
128
ed1e77d3 129 injectedScript.evaluate(errorString, expression, objectGroup ? *objectGroup : String(), asBool(includeCommandLineAPI), asBool(returnByValue), asBool(generatePreview), asBool(saveResult), &result, wasThrown, savedResultIndex);
81345200
A
130
131 if (asBool(doNotPauseOnExceptionsAndMuteConsole)) {
132 unmuteConsole();
133 setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState);
134 }
135}
136
ed1e77d3 137void 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)
81345200
A
138{
139 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
140 if (injectedScript.hasNoValue()) {
ed1e77d3 141 errorString = ASCIILiteral("Inspected frame has gone");
81345200
A
142 return;
143 }
144
145 String arguments;
146 if (optionalArguments)
ed1e77d3 147 arguments = optionalArguments->toJSONString();
81345200
A
148
149 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = ScriptDebugServer::DontPauseOnExceptions;
150 if (asBool(doNotPauseOnExceptionsAndMuteConsole))
151 previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions);
152 if (asBool(doNotPauseOnExceptionsAndMuteConsole))
153 muteConsole();
154
155 injectedScript.callFunctionOn(errorString, objectId, expression, arguments, asBool(returnByValue), asBool(generatePreview), &result, wasThrown);
156
157 if (asBool(doNotPauseOnExceptionsAndMuteConsole)) {
158 unmuteConsole();
159 setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState);
160 }
161}
162
ed1e77d3
A
163void 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)
164{
165 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
166 if (injectedScript.hasNoValue()) {
167 errorString = ASCIILiteral("Inspected frame has gone");
168 return;
169 }
170
171 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions);
172 muteConsole();
173
174 injectedScript.getProperties(errorString, objectId, asBool(ownProperties), asBool(generatePreview), &result);
175 injectedScript.getInternalProperties(errorString, objectId, asBool(generatePreview), &internalProperties);
176
177 unmuteConsole();
178 setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState);
179}
180
181void 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)
81345200
A
182{
183 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
184 if (injectedScript.hasNoValue()) {
ed1e77d3 185 errorString = ASCIILiteral("Inspected frame has gone");
81345200
A
186 return;
187 }
188
189 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions);
190 muteConsole();
191
ed1e77d3
A
192 injectedScript.getDisplayableProperties(errorString, objectId, asBool(generatePreview), &result);
193 injectedScript.getInternalProperties(errorString, objectId, asBool(generatePreview), &internalProperties);
81345200
A
194
195 unmuteConsole();
196 setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState);
197}
198
ed1e77d3
A
199void 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)
200{
201 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
202 if (injectedScript.hasNoValue()) {
203 errorString = ASCIILiteral("Inspected frame has gone");
204 return;
205 }
206
207 int start = startIndex && *startIndex >= 0 ? *startIndex : 0;
208 int fetch = numberToFetch && *numberToFetch >= 0 ? *numberToFetch : 0;
209
210 injectedScript.getCollectionEntries(errorString, objectId, objectGroup ? *objectGroup : String(), start, fetch, &entries);
211}
212
213void InspectorRuntimeAgent::saveResult(ErrorString& errorString, const Inspector::InspectorObject& callArgument, const int* executionContextId, Inspector::Protocol::OptOutput<int>* savedResultIndex)
214{
215 InjectedScript injectedScript;
216
217 String objectId;
218 if (callArgument.getString(ASCIILiteral("objectId"), objectId)) {
219 injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
220 if (injectedScript.hasNoValue()) {
221 errorString = ASCIILiteral("Inspected frame has gone");
222 return;
223 }
224 } else {
225 injectedScript = injectedScriptForEval(errorString, executionContextId);
226 if (injectedScript.hasNoValue())
227 return;
228 }
229
230 injectedScript.saveResult(errorString, callArgument.toJSONString(), savedResultIndex);
231}
232
233void InspectorRuntimeAgent::releaseObject(ErrorString&, const String& objectId)
81345200
A
234{
235 InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
236 if (!injectedScript.hasNoValue())
237 injectedScript.releaseObject(objectId);
238}
239
ed1e77d3 240void InspectorRuntimeAgent::releaseObjectGroup(ErrorString&, const String& objectGroup)
81345200
A
241{
242 m_injectedScriptManager->releaseObjectGroup(objectGroup);
243}
244
ed1e77d3 245void InspectorRuntimeAgent::run(ErrorString&)
81345200 246{
ed1e77d3 247 // FIXME: <https://webkit.org/b/127634> Web Inspector: support debugging web workers
81345200
A
248}
249
ed1e77d3
A
250void InspectorRuntimeAgent::getRuntimeTypesForVariablesAtOffsets(ErrorString& errorString, const Inspector::InspectorArray& locations, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::TypeDescription>>& typeDescriptions)
251{
252 static const bool verbose = false;
253 VM& vm = globalVM();
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.");
257 return;
258 }
259
260 double start = currentTimeMS();
261 vm.typeProfilerLog()->processLogEntries(ASCIILiteral("User Query"));
262
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.");
268 return;
269 }
270
271 int descriptor;
272 String sourceIDAsString;
273 int divot;
274 location->getInteger(ASCIILiteral("typeInformationDescriptor"), descriptor);
275 location->getString(ASCIILiteral("sourceID"), sourceIDAsString);
276 location->getInteger(ASCIILiteral("divot"), divot);
277
278 bool okay;
279 TypeLocation* typeLocation = vm.typeProfiler()->findLocation(divot, sourceIDAsString.toIntPtrStrict(&okay), static_cast<TypeProfilerSearchDescriptor>(descriptor), vm);
280 ASSERT(okay);
281
282 RefPtr<TypeSet> typeSet;
283 if (typeLocation) {
284 if (typeLocation->m_globalTypeSet && typeLocation->m_globalVariableID != TypeProfilerNoGlobalIDExists)
285 typeSet = typeLocation->m_globalTypeSet;
286 else
287 typeSet = typeLocation->m_instructionTypeSet;
288 }
289
290 bool isValid = typeLocation && typeSet && !typeSet->isEmpty();
291 auto description = Inspector::Protocol::Runtime::TypeDescription::create()
292 .setIsValid(isValid)
293 .release();
294
295 if (isValid) {
296 description->setLeastCommonAncestor(typeSet->leastCommonAncestor());
297 description->setStructures(typeSet->allStructureRepresentations());
298 description->setTypeSet(typeSet->inspectorTypeSet());
299 description->setIsTruncated(typeSet->isOverflown());
300 }
301
302 typeDescriptions->addItem(WTF::move(description));
303 }
81345200 304
ed1e77d3
A
305 double end = currentTimeMS();
306 if (verbose)
307 dataLogF("Inspector::getRuntimeTypesForVariablesAtOffsets took %lfms\n", end - start);
308}
309
310class TypeRecompiler : public MarkedBlock::VoidFunctor {
311public:
312 inline void visit(JSCell* cell)
313 {
314 if (!cell->inherits(FunctionExecutable::info()))
315 return;
316
317 FunctionExecutable* executable = jsCast<FunctionExecutable*>(cell);
318 executable->clearCode();
319 executable->clearUnlinkedCodeForRecompilation();
320 }
321 inline IterationStatus operator()(JSCell* cell)
322 {
323 visit(cell);
324 return IterationStatus::Continue;
325 }
326};
327
328static void recompileAllJSFunctionsForTypeProfiling(VM& vm, bool shouldEnableTypeProfiling)
329{
330 bool shouldRecompileFromTypeProfiler = (shouldEnableTypeProfiling ? vm.enableTypeProfiler() : vm.disableTypeProfiler());
331 bool shouldRecompileFromControlFlowProfiler = (shouldEnableTypeProfiling ? vm.enableControlFlowProfiler() : vm.disableControlFlowProfiler());
332 bool needsToRecompile = shouldRecompileFromTypeProfiler || shouldRecompileFromControlFlowProfiler;
333
334 if (needsToRecompile) {
335 vm.prepareToDiscardCode();
336 TypeRecompiler recompiler;
337 HeapIterationScope iterationScope(vm.heap);
338 vm.heap.objectSpace().forEachLiveCell(iterationScope, recompiler);
339 }
340}
341
342void InspectorRuntimeAgent::willDestroyFrontendAndBackend(DisconnectReason reason)
343{
344 if (reason != DisconnectReason::InspectedTargetDestroyed && m_isTypeProfilingEnabled)
345 setTypeProfilerEnabledState(false);
346}
347
348void InspectorRuntimeAgent::enableTypeProfiler(ErrorString&)
349{
350 setTypeProfilerEnabledState(true);
351}
352
353void InspectorRuntimeAgent::disableTypeProfiler(ErrorString&)
354{
355 setTypeProfilerEnabledState(false);
356}
357
358void InspectorRuntimeAgent::setTypeProfilerEnabledState(bool shouldEnableTypeProfiling)
359{
360 if (m_isTypeProfilingEnabled == shouldEnableTypeProfiling)
361 return;
362
363 m_isTypeProfilingEnabled = shouldEnableTypeProfiling;
364
365 VM& vm = globalVM();
366
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.
369 if (vm.entryScope) {
370 vm.entryScope->setEntryScopeDidPopListener(this,
371 [=] (VM& vm, JSGlobalObject*) {
372 recompileAllJSFunctionsForTypeProfiling(vm, shouldEnableTypeProfiling);
373 }
374 );
375 } else
376 recompileAllJSFunctionsForTypeProfiling(vm, shouldEnableTypeProfiling);
377}
378
379void InspectorRuntimeAgent::getBasicBlocks(ErrorString& errorString, const String& sourceIDAsString, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::BasicBlock>>& basicBlocks)
380{
381 VM& vm = globalVM();
382 if (!vm.controlFlowProfiler()) {
383 errorString = ASCIILiteral("The VM does not currently have a Control Flow Profiler.");
384 return;
385 }
386
387 bool okay;
388 intptr_t sourceID = sourceIDAsString.toIntPtrStrict(&okay);
389 ASSERT(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)
397 .release();
398 basicBlocks->addItem(WTF::move(location));
399 }
400}
401
402} // namespace Inspector