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