Web Inspector: Save Console Evaluations into Command Line variables $1-$99 ($n)
[WebKit-https.git] / Source / JavaScriptCore / inspector / agents / InspectorRuntimeAgent.cpp
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
35 #include "Completion.h"
36 #include "HeapIterationScope.h"
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"
44 #include "TypeProfiler.h"
45 #include "TypeProfilerLog.h"
46 #include "VMEntryScope.h"
47 #include <wtf/CurrentTime.h>
48
49 using namespace JSC;
50
51 namespace Inspector {
52
53 static bool asBool(const bool* const b)
54 {
55     return b ? *b : false;
56 }
57
58 InspectorRuntimeAgent::InspectorRuntimeAgent(InjectedScriptManager* injectedScriptManager)
59     : InspectorAgentBase(ASCIILiteral("Runtime"))
60     , m_injectedScriptManager(injectedScriptManager)
61     , m_scriptDebugServer(nullptr)
62     , m_enabled(false)
63     , m_isTypeProfilingEnabled(false)
64 {
65 }
66
67 InspectorRuntimeAgent::~InspectorRuntimeAgent()
68 {
69 }
70
71 static 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
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     VM& vm = globalVM();
91     JSLockHolder lock(vm);
92
93     ParserError error;
94     checkSyntax(vm, JSC::makeSource(expression), error);
95
96     switch (error.syntaxErrorType()) {
97     case ParserError::SyntaxErrorNone:
98         *result = Inspector::Protocol::Runtime::SyntaxErrorType::None;
99         break;
100     case ParserError::SyntaxErrorIrrecoverable:
101         *result = Inspector::Protocol::Runtime::SyntaxErrorType::Irrecoverable;
102         break;
103     case ParserError::SyntaxErrorUnterminatedLiteral:
104         *result = Inspector::Protocol::Runtime::SyntaxErrorType::UnterminatedLiteral;
105         break;
106     case ParserError::SyntaxErrorRecoverable:
107         *result = Inspector::Protocol::Runtime::SyntaxErrorType::Recoverable;
108         break;
109     }
110
111     if (error.syntaxErrorType() != ParserError::SyntaxErrorNone) {
112         *message = error.message();
113         range = buildErrorRangeObject(error.token().m_location);
114     }
115 }
116
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)
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
129     injectedScript.evaluate(errorString, expression, objectGroup ? *objectGroup : String(), asBool(includeCommandLineAPI), asBool(returnByValue), asBool(generatePreview), asBool(saveResult), &result, wasThrown, savedResultIndex);
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 RefPtr<InspectorArray>&& optionalArguments, const bool* const doNotPauseOnExceptionsAndMuteConsole, const bool* const returnByValue, const bool* generatePreview, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result, Inspector::Protocol::OptOutput<bool>* wasThrown)
138 {
139     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
140     if (injectedScript.hasNoValue()) {
141         errorString = ASCIILiteral("Inspected frame has gone");
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     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
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)
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
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)
182 {
183     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
184     if (injectedScript.hasNoValue()) {
185         errorString = ASCIILiteral("Inspected frame has gone");
186         return;
187     }
188
189     ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions);
190     muteConsole();
191
192     injectedScript.getDisplayableProperties(errorString, objectId, asBool(generatePreview), &result);
193     injectedScript.getInternalProperties(errorString, objectId, asBool(generatePreview), &internalProperties);
194
195     unmuteConsole();
196     setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState);
197 }
198
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)
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
213 void InspectorRuntimeAgent::releaseObject(ErrorString&, const String& objectId)
214 {
215     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
216     if (!injectedScript.hasNoValue())
217         injectedScript.releaseObject(objectId);
218 }
219
220 void InspectorRuntimeAgent::releaseObjectGroup(ErrorString&, const String& objectGroup)
221 {
222     m_injectedScriptManager->releaseObjectGroup(objectGroup);
223 }
224
225 void InspectorRuntimeAgent::run(ErrorString&)
226 {
227     // FIXME: <https://webkit.org/b/127634> Web Inspector: support debugging web workers
228 }
229
230 void InspectorRuntimeAgent::getRuntimeTypesForVariablesAtOffsets(ErrorString& errorString, const RefPtr<Inspector::InspectorArray>&& locations, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::TypeDescription>>& typeDescriptions)
231 {
232     static const bool verbose = false;
233     VM& vm = globalVM();
234     typeDescriptions = Inspector::Protocol::Array<Inspector::Protocol::Runtime::TypeDescription>::create();
235     if (!vm.typeProfiler()) {
236         errorString = ASCIILiteral("The VM does not currently have Type Information.");
237         return;
238     }
239
240     double start = currentTimeMS();
241     vm.typeProfilerLog()->processLogEntries(ASCIILiteral("User Query"));
242
243     for (size_t i = 0; i < locations->length(); i++) {
244         RefPtr<Inspector::InspectorValue> value = locations->get(i);
245         RefPtr<InspectorObject> location;
246         if (!value->asObject(location)) {
247             errorString = ASCIILiteral("Array of TypeLocation objects has an object that does not have type of TypeLocation.");
248             return;
249         }
250
251         int descriptor;
252         String sourceIDAsString;
253         int divot;
254         location->getInteger(ASCIILiteral("typeInformationDescriptor"), descriptor);
255         location->getString(ASCIILiteral("sourceID"), sourceIDAsString);
256         location->getInteger(ASCIILiteral("divot"), divot);
257
258         bool okay;
259         TypeLocation* typeLocation = vm.typeProfiler()->findLocation(divot, sourceIDAsString.toIntPtrStrict(&okay), static_cast<TypeProfilerSearchDescriptor>(descriptor), vm);
260         ASSERT(okay);
261
262         RefPtr<TypeSet> typeSet;
263         if (typeLocation) {
264             if (typeLocation->m_globalTypeSet && typeLocation->m_globalVariableID != TypeProfilerNoGlobalIDExists)
265                 typeSet = typeLocation->m_globalTypeSet;
266             else
267                 typeSet = typeLocation->m_instructionTypeSet;
268         }
269
270         bool isValid = typeLocation && typeSet && !typeSet->isEmpty();
271         auto description = Inspector::Protocol::Runtime::TypeDescription::create()
272             .setIsValid(isValid)
273             .release();
274
275         if (isValid) {
276             description->setLeastCommonAncestor(typeSet->leastCommonAncestor());
277             description->setStructures(typeSet->allStructureRepresentations());
278             description->setTypeSet(typeSet->inspectorTypeSet());
279             description->setIsTruncated(typeSet->isOverflown());
280         }
281
282         typeDescriptions->addItem(WTF::move(description));
283     }
284
285     double end = currentTimeMS();
286     if (verbose)
287         dataLogF("Inspector::getRuntimeTypesForVariablesAtOffsets took %lfms\n", end - start);
288 }
289
290 class TypeRecompiler : public MarkedBlock::VoidFunctor {
291 public:
292     inline void operator()(JSCell* cell)
293     {
294         if (!cell->inherits(FunctionExecutable::info()))
295             return;
296
297         FunctionExecutable* executable = jsCast<FunctionExecutable*>(cell);
298         executable->clearCodeIfNotCompiling();
299         executable->clearUnlinkedCodeForRecompilationIfNotCompiling();
300     }
301 };
302
303 static void recompileAllJSFunctionsForTypeProfiling(VM& vm, bool shouldEnableTypeProfiling)
304 {
305     bool shouldRecompileFromTypeProfiler = (shouldEnableTypeProfiling ? vm.enableTypeProfiler() : vm.disableTypeProfiler());
306     bool shouldRecompileFromControlFlowProfiler = (shouldEnableTypeProfiling ? vm.enableControlFlowProfiler() : vm.disableControlFlowProfiler());
307     bool needsToRecompile = shouldRecompileFromTypeProfiler || shouldRecompileFromControlFlowProfiler;
308
309     if (needsToRecompile) {
310         vm.prepareToDiscardCode();
311         TypeRecompiler recompiler;
312         HeapIterationScope iterationScope(vm.heap);
313         vm.heap.objectSpace().forEachLiveCell(iterationScope, recompiler);
314     }
315 }
316
317 void InspectorRuntimeAgent::willDestroyFrontendAndBackend(DisconnectReason reason)
318 {
319     if (reason != DisconnectReason::InspectedTargetDestroyed && m_isTypeProfilingEnabled)
320         setTypeProfilerEnabledState(false);
321 }
322
323 void InspectorRuntimeAgent::enableTypeProfiler(ErrorString&)
324 {
325     setTypeProfilerEnabledState(true);
326 }
327
328 void InspectorRuntimeAgent::disableTypeProfiler(ErrorString&)
329 {
330     setTypeProfilerEnabledState(false);
331 }
332
333 void InspectorRuntimeAgent::setTypeProfilerEnabledState(bool shouldEnableTypeProfiling)
334 {
335     if (m_isTypeProfilingEnabled == shouldEnableTypeProfiling)
336         return;
337
338     m_isTypeProfilingEnabled = shouldEnableTypeProfiling;
339
340     VM& vm = globalVM();
341
342     // If JavaScript is running, it's not safe to recompile, since we'll end
343     // up throwing away code that is live on the stack.
344     if (vm.entryScope) {
345         vm.entryScope->setEntryScopeDidPopListener(this,
346             [=] (VM& vm, JSGlobalObject*) {
347                 recompileAllJSFunctionsForTypeProfiling(vm, shouldEnableTypeProfiling);
348             }
349         );
350     } else
351         recompileAllJSFunctionsForTypeProfiling(vm, shouldEnableTypeProfiling);
352 }
353
354 void InspectorRuntimeAgent::getBasicBlocks(ErrorString& errorString, const String& sourceIDAsString, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::BasicBlock>>& basicBlocks)
355 {
356     VM& vm = globalVM();
357     if (!vm.controlFlowProfiler()) {
358         errorString = ASCIILiteral("The VM does not currently have a Control Flow Profiler.");
359         return;
360     }
361
362     bool okay;
363     intptr_t sourceID = sourceIDAsString.toIntPtrStrict(&okay);
364     ASSERT(okay);
365     const Vector<BasicBlockRange>& basicBlockRanges = vm.controlFlowProfiler()->getBasicBlocksForSourceID(sourceID, vm);
366     basicBlocks = Inspector::Protocol::Array<Inspector::Protocol::Runtime::BasicBlock>::create();
367     for (const BasicBlockRange& block : basicBlockRanges) {
368         Ref<Inspector::Protocol::Runtime::BasicBlock> location = Inspector::Protocol::Runtime::BasicBlock::create()
369             .setStartOffset(block.m_startOffset)
370             .setEndOffset(block.m_endOffset)
371             .setHasExecuted(block.m_hasExecuted)
372             .release();
373         basicBlocks->addItem(WTF::move(location));
374     }
375 }
376
377 } // namespace Inspector