1/*
2 * Copyright (C) 2013-2015 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
35#include "Completion.h"
36#include "DFGWorklist.h"
37#include "HeapIterationScope.h"
38#include "InjectedScript.h"
39#include "InjectedScriptManager.h"
40#include "InspectorFrontendRouter.h"
41#include "JSLock.h"
42#include "ParserError.h"
43#include "ScriptDebugServer.h"
44#include "SourceCode.h"
45#include "TypeProfiler.h"
46#include "TypeProfilerLog.h"
47#include <wtf/JSONValues.h>
48
49namespace Inspector {
50
51using namespace JSC;
52
53static bool asBool(const bool* b)
54{
55 return b && *b;
56}
57
58InspectorRuntimeAgent::InspectorRuntimeAgent(AgentContext& context)
59 : InspectorAgentBase("Runtime"_s)
60 , m_injectedScriptManager(context.injectedScriptManager)
61 , m_scriptDebugServer(context.environment.scriptDebugServer())
62 , m_vm(context.environment.vm())
63{
64}
65
66InspectorRuntimeAgent::~InspectorRuntimeAgent()
67{
68}
69
70static ScriptDebugServer::PauseOnExceptionsState setPauseOnExceptionsState(ScriptDebugServer& scriptDebugServer, ScriptDebugServer::PauseOnExceptionsState newState)
71{
72 auto presentState = scriptDebugServer.pauseOnExceptionsState();
73 if (presentState != newState)
74 scriptDebugServer.setPauseOnExceptionsState(newState);
75 return presentState;
76}
77
78static Ref<Protocol::Runtime::ErrorRange> buildErrorRangeObject(const JSTokenLocation& tokenLocation)
79{
80 return Protocol::Runtime::ErrorRange::create()
81 .setStartOffset(tokenLocation.startOffset)
82 .setEndOffset(tokenLocation.endOffset)
83 .release();
84}
85
86void InspectorRuntimeAgent::parse(ErrorString&, const String& expression, Protocol::Runtime::SyntaxErrorType* result, Optional<String>& message, RefPtr<Protocol::Runtime::ErrorRange>& range)
87{
88 JSLockHolder lock(m_vm);
89
90 ParserError error;
91 checkSyntax(m_vm, JSC::makeSource(expression, { }), error);
92
93 switch (error.syntaxErrorType()) {
94 case ParserError::SyntaxErrorNone:
95 *result = Protocol::Runtime::SyntaxErrorType::None;
96 break;
97 case ParserError::SyntaxErrorIrrecoverable:
98 *result = Protocol::Runtime::SyntaxErrorType::Irrecoverable;
99 break;
100 case ParserError::SyntaxErrorUnterminatedLiteral:
101 *result = Protocol::Runtime::SyntaxErrorType::UnterminatedLiteral;
102 break;
103 case ParserError::SyntaxErrorRecoverable:
104 *result = Protocol::Runtime::SyntaxErrorType::Recoverable;
105 break;
106 }
107
108 if (error.syntaxErrorType() != ParserError::SyntaxErrorNone) {
109 message = error.message();
110 range = buildErrorRangeObject(error.token().m_location);
111 }
112}
113
114void InspectorRuntimeAgent::evaluate(ErrorString& errorString, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const int* executionContextId, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, const bool* /* emulateUserGesture */, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown, Optional<int>& savedResultIndex)
115{
116 InjectedScript injectedScript = injectedScriptForEval(errorString, executionContextId);
117 if (injectedScript.hasNoValue())
118 return;
119
120 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = ScriptDebugServer::DontPauseOnExceptions;
121 if (asBool(doNotPauseOnExceptionsAndMuteConsole))
122 previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions);
123 if (asBool(doNotPauseOnExceptionsAndMuteConsole))
124 muteConsole();
125
126 injectedScript.evaluate(errorString, expression, objectGroup ? *objectGroup : String(), asBool(includeCommandLineAPI), asBool(returnByValue), asBool(generatePreview), asBool(saveResult), result, wasThrown, savedResultIndex);
127
128 if (asBool(doNotPauseOnExceptionsAndMuteConsole)) {
129 unmuteConsole();
130 setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState);
131 }
132}
133
134void InspectorRuntimeAgent::awaitPromise(const String& promiseObjectId, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, Ref<AwaitPromiseCallback>&& callback)
135{
136 InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(promiseObjectId);
137 if (injectedScript.hasNoValue()) {
138 callback->sendFailure("Could not find InjectedScript for promiseObjectId"_s);
139 return;
140 }
141
142 injectedScript.awaitPromise(promiseObjectId, asBool(returnByValue), asBool(generatePreview), asBool(saveResult), [callback = WTFMove(callback)] (ErrorString& errorString, RefPtr<Protocol::Runtime::RemoteObject>&& result, Optional<bool>& wasThrown, Optional<int>& savedResultIndex) {
143 if (!errorString.isEmpty())
144 callback->sendFailure(errorString);
145 else
146 callback->sendSuccess(WTFMove(result), wasThrown, savedResultIndex);
147 });
148}
149
150void InspectorRuntimeAgent::callFunctionOn(ErrorString& errorString, const String& objectId, const String& expression, const JSON::Array* optionalArguments, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown)
151{
152 InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId);
153 if (injectedScript.hasNoValue()) {
154 errorString = "Could not find InjectedScript for objectId"_s;
155 return;
156 }
157
158 String arguments;
159 if (optionalArguments)
160 arguments = optionalArguments->toJSONString();
161
162 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = ScriptDebugServer::DontPauseOnExceptions;
163 if (asBool(doNotPauseOnExceptionsAndMuteConsole))
164 previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions);
165 if (asBool(doNotPauseOnExceptionsAndMuteConsole))
166 muteConsole();
167
168 injectedScript.callFunctionOn(errorString, objectId, expression, arguments, asBool(returnByValue), asBool(generatePreview), result, wasThrown);
169
170 if (asBool(doNotPauseOnExceptionsAndMuteConsole)) {
171 unmuteConsole();
172 setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState);
173 }
174}
175
176void InspectorRuntimeAgent::getPreview(ErrorString& errorString, const String& objectId, RefPtr<Protocol::Runtime::ObjectPreview>& preview)
177{
178 InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId);
179 if (injectedScript.hasNoValue()) {
180 errorString = "Could not find InjectedScript for objectId"_s;
181 return;
182 }
183
184 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions);
185 muteConsole();
186
187 injectedScript.getPreview(errorString, objectId, preview);
188
189 unmuteConsole();
190 setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState);
191}
192
193void InspectorRuntimeAgent::getProperties(ErrorString& errorString, const String& objectId, const bool* ownProperties, const bool* generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& result, RefPtr<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>& internalProperties)
194{
195 InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId);
196 if (injectedScript.hasNoValue()) {
197 errorString = "Could not find InjectedScript for objectId"_s;
198 return;
199 }
200
201 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions);
202 muteConsole();
203
204 injectedScript.getProperties(errorString, objectId, asBool(ownProperties), asBool(generatePreview), result);
205 injectedScript.getInternalProperties(errorString, objectId, asBool(generatePreview), internalProperties);
206
207 unmuteConsole();
208 setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState);
209}
210
211void InspectorRuntimeAgent::getDisplayableProperties(ErrorString& errorString, const String& objectId, const bool* generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& result, RefPtr<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>& internalProperties)
212{
213 InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId);
214 if (injectedScript.hasNoValue()) {
215 errorString = "Could not find InjectedScript for objectId"_s;
216 return;
217 }
218
219 ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions);
220 muteConsole();
221
222 injectedScript.getDisplayableProperties(errorString, objectId, asBool(generatePreview), result);
223 injectedScript.getInternalProperties(errorString, objectId, asBool(generatePreview), internalProperties);
224
225 unmuteConsole();
226 setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState);
227}
228
229void InspectorRuntimeAgent::getCollectionEntries(ErrorString& errorString, const String& objectId, const String* objectGroup, const int* startIndex, const int* numberToFetch, RefPtr<JSON::ArrayOf<Protocol::Runtime::CollectionEntry>>& entries)
230{
231 InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId);
232 if (injectedScript.hasNoValue()) {
233 errorString = "Could not find InjectedScript for objectId"_s;
234 return;
235 }
236
237 int start = startIndex && *startIndex >= 0 ? *startIndex : 0;
238 int fetch = numberToFetch && *numberToFetch >= 0 ? *numberToFetch : 0;
239
240 injectedScript.getCollectionEntries(errorString, objectId, objectGroup ? *objectGroup : String(), start, fetch, entries);
241}
242
243void InspectorRuntimeAgent::saveResult(ErrorString& errorString, const JSON::Object& callArgument, const int* executionContextId, Optional<int>& savedResultIndex)
244{
245 InjectedScript injectedScript;
246
247 String objectId;
248 if (callArgument.getString("objectId"_s, objectId)) {
249 injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId);
250 if (injectedScript.hasNoValue()) {
251 errorString = "Could not find InjectedScript for objectId"_s;
252 return;
253 }
254 } else {
255 injectedScript = injectedScriptForEval(errorString, executionContextId);
256 if (injectedScript.hasNoValue())
257 return;
258 }
259
260 injectedScript.saveResult(errorString, callArgument.toJSONString(), savedResultIndex);
261}
262
263void InspectorRuntimeAgent::releaseObject(ErrorString&, const String& objectId)
264{
265 InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId);
266 if (!injectedScript.hasNoValue())
267 injectedScript.releaseObject(objectId);
268}
269
270void InspectorRuntimeAgent::releaseObjectGroup(ErrorString&, const String& objectGroup)
271{
272 m_injectedScriptManager.releaseObjectGroup(objectGroup);
273}
274
275void InspectorRuntimeAgent::getRuntimeTypesForVariablesAtOffsets(ErrorString& errorString, const JSON::Array& locations, RefPtr<JSON::ArrayOf<Protocol::Runtime::TypeDescription>>& typeDescriptions)
276{
277 static const bool verbose = false;
278
279 typeDescriptions = JSON::ArrayOf<Protocol::Runtime::TypeDescription>::create();
280 if (!m_vm.typeProfiler()) {
281 errorString = "The VM does not currently have Type Information."_s;
282 return;
283 }
284
285 MonotonicTime start = MonotonicTime::now();
286 m_vm.typeProfilerLog()->processLogEntries(m_vm, "User Query"_s);
287
288 for (size_t i = 0; i < locations.length(); i++) {
289 RefPtr<JSON::Value> value = locations.get(i);
290 RefPtr<JSON::Object> location;
291 if (!value->asObject(location)) {
292 errorString = "Array of TypeLocation objects has an object that does not have type of TypeLocation."_s;
293 return;
294 }
295
296 int descriptor;
297 String sourceIDAsString;
298 int divot;
299 location->getInteger("typeInformationDescriptor"_s, descriptor);
300 location->getString("sourceID"_s, sourceIDAsString);
301 location->getInteger("divot"_s, divot);
302
303 bool okay;
304 TypeLocation* typeLocation = m_vm.typeProfiler()->findLocation(divot, sourceIDAsString.toIntPtrStrict(&okay), static_cast<TypeProfilerSearchDescriptor>(descriptor), m_vm);
305 ASSERT(okay);
306
307 RefPtr<TypeSet> typeSet;
308 if (typeLocation) {
309 if (typeLocation->m_globalTypeSet && typeLocation->m_globalVariableID != TypeProfilerNoGlobalIDExists)
310 typeSet = typeLocation->m_globalTypeSet;
311 else
312 typeSet = typeLocation->m_instructionTypeSet;
313 }
314
315 bool isValid = typeLocation && typeSet && !typeSet->isEmpty();
316 auto description = Protocol::Runtime::TypeDescription::create()
317 .setIsValid(isValid)
318 .release();
319
320 if (isValid) {
321 description->setLeastCommonAncestor(typeSet->leastCommonAncestor());
322 description->setStructures(typeSet->allStructureRepresentations());
323 description->setTypeSet(typeSet->inspectorTypeSet());
324 description->setIsTruncated(typeSet->isOverflown());
325 }
326
327 typeDescriptions->addItem(WTFMove(description));
328 }
329
330 MonotonicTime end = MonotonicTime::now();
331 if (verbose)
332 dataLogF("Inspector::getRuntimeTypesForVariablesAtOffsets took %lfms\n", (end - start).milliseconds());
333}
334
335void InspectorRuntimeAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
336{
337}
338
339void InspectorRuntimeAgent::willDestroyFrontendAndBackend(DisconnectReason reason)
340{
341 if (reason != DisconnectReason::InspectedTargetDestroyed && m_isTypeProfilingEnabled)
342 setTypeProfilerEnabledState(false);
343
344 String unused;
345 disable(unused);
346}
347
348void InspectorRuntimeAgent::enableTypeProfiler(ErrorString&)
349{
350 setTypeProfilerEnabledState(true);
351}
352
353void InspectorRuntimeAgent::disableTypeProfiler(ErrorString&)
354{
355 setTypeProfilerEnabledState(false);
356}
357
358void InspectorRuntimeAgent::enableControlFlowProfiler(ErrorString&)
359{
360 setControlFlowProfilerEnabledState(true);
361}
362
363void InspectorRuntimeAgent::disableControlFlowProfiler(ErrorString&)
364{
365 setControlFlowProfilerEnabledState(false);
366}
367
368void InspectorRuntimeAgent::setTypeProfilerEnabledState(bool isTypeProfilingEnabled)
369{
370 if (m_isTypeProfilingEnabled == isTypeProfilingEnabled)
371 return;
372 m_isTypeProfilingEnabled = isTypeProfilingEnabled;
373
374 VM& vm = m_vm;
375 vm.whenIdle([&vm, isTypeProfilingEnabled] () {
376 bool shouldRecompileFromTypeProfiler = (isTypeProfilingEnabled ? vm.enableTypeProfiler() : vm.disableTypeProfiler());
377 if (shouldRecompileFromTypeProfiler)
378 vm.deleteAllCode(PreventCollectionAndDeleteAllCode);
379 });
380}
381
382void InspectorRuntimeAgent::setControlFlowProfilerEnabledState(bool isControlFlowProfilingEnabled)
383{
384 if (m_isControlFlowProfilingEnabled == isControlFlowProfilingEnabled)
385 return;
386 m_isControlFlowProfilingEnabled = isControlFlowProfilingEnabled;
387
388 VM& vm = m_vm;
389 vm.whenIdle([&vm, isControlFlowProfilingEnabled] () {
390 bool shouldRecompileFromControlFlowProfiler = (isControlFlowProfilingEnabled ? vm.enableControlFlowProfiler() : vm.disableControlFlowProfiler());
391
392 if (shouldRecompileFromControlFlowProfiler)
393 vm.deleteAllCode(PreventCollectionAndDeleteAllCode);
394 });
395}
396
397void InspectorRuntimeAgent::getBasicBlocks(ErrorString& errorString, const String& sourceIDAsString, RefPtr<JSON::ArrayOf<Protocol::Runtime::BasicBlock>>& basicBlocks)
398{
399 if (!m_vm.controlFlowProfiler()) {
400 errorString = "The VM does not currently have a Control Flow Profiler."_s;
401 return;
402 }
403
404 bool okay;
405 intptr_t sourceID = sourceIDAsString.toIntPtrStrict(&okay);
406 ASSERT(okay);
407 const Vector<BasicBlockRange>& basicBlockRanges = m_vm.controlFlowProfiler()->getBasicBlocksForSourceID(sourceID, m_vm);
408 basicBlocks = JSON::ArrayOf<Protocol::Runtime::BasicBlock>::create();
409 for (const BasicBlockRange& block : basicBlockRanges) {
410 auto location = Protocol::Runtime::BasicBlock::create()
411 .setStartOffset(block.m_startOffset)
412 .setEndOffset(block.m_endOffset)
413 .setHasExecuted(block.m_hasExecuted)
414 .setExecutionCount(block.m_executionCount)
415 .release();
416 basicBlocks->addItem(WTFMove(location));
417 }
418}
419
420} // namespace Inspector
421