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