80a0d9f3e285965996c777174dd9f45795663cdc
[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 #if ENABLE(INSPECTOR)
36
37 #include "Completion.h"
38 #include "HeapIterationScope.h"
39 #include "InjectedScript.h"
40 #include "InjectedScriptManager.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/PassRefPtr.h>
50 #include <wtf/CurrentTime.h>
51
52 using namespace JSC;
53
54 namespace Inspector {
55
56 static bool asBool(const bool* const b)
57 {
58     return b ? *b : false;
59 }
60
61 InspectorRuntimeAgent::InspectorRuntimeAgent(InjectedScriptManager* injectedScriptManager)
62     : InspectorAgentBase(ASCIILiteral("Runtime"))
63     , m_injectedScriptManager(injectedScriptManager)
64     , m_scriptDebugServer(nullptr)
65     , m_enabled(false)
66     , m_isTypeProfilingEnabled(false)
67 {
68 }
69
70 InspectorRuntimeAgent::~InspectorRuntimeAgent()
71 {
72 }
73
74 static ScriptDebugServer::PauseOnExceptionsState setPauseOnExceptionsState(ScriptDebugServer* scriptDebugServer, ScriptDebugServer::PauseOnExceptionsState newState)
75 {
76     ASSERT(scriptDebugServer);
77     ScriptDebugServer::PauseOnExceptionsState presentState = scriptDebugServer->pauseOnExceptionsState();
78     if (presentState != newState)
79         scriptDebugServer->setPauseOnExceptionsState(newState);
80     return presentState;
81 }
82
83 static PassRefPtr<Inspector::Protocol::Runtime::ErrorRange> buildErrorRangeObject(const JSTokenLocation& tokenLocation)
84 {
85     RefPtr<Inspector::Protocol::Runtime::ErrorRange> result = Inspector::Protocol::Runtime::ErrorRange::create()
86         .setStartOffset(tokenLocation.startOffset)
87         .setEndOffset(tokenLocation.endOffset);
88     return result.release();
89 }
90
91 void InspectorRuntimeAgent::parse(ErrorString&, const String& expression, Inspector::Protocol::Runtime::SyntaxErrorType* result, Inspector::Protocol::OptOutput<String>* message, RefPtr<Inspector::Protocol::Runtime::ErrorRange>& range)
92 {
93     VM& vm = globalVM();
94     JSLockHolder lock(vm);
95
96     ParserError error;
97     checkSyntax(vm, JSC::makeSource(expression), error);
98
99     switch (error.m_syntaxErrorType) {
100     case ParserError::SyntaxErrorNone:
101         *result = Inspector::Protocol::Runtime::SyntaxErrorType::None;
102         break;
103     case ParserError::SyntaxErrorIrrecoverable:
104         *result = Inspector::Protocol::Runtime::SyntaxErrorType::Irrecoverable;
105         break;
106     case ParserError::SyntaxErrorUnterminatedLiteral:
107         *result = Inspector::Protocol::Runtime::SyntaxErrorType::UnterminatedLiteral;
108         break;
109     case ParserError::SyntaxErrorRecoverable:
110         *result = Inspector::Protocol::Runtime::SyntaxErrorType::Recoverable;
111         break;
112     }
113
114     if (error.m_syntaxErrorType != ParserError::SyntaxErrorNone) {
115         *message = error.m_message;
116         range = buildErrorRangeObject(error.m_token.m_location);
117     }
118 }
119
120 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, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result, Inspector::Protocol::OptOutput<bool>* wasThrown)
121 {
122     InjectedScript injectedScript = injectedScriptForEval(errorString, executionContextId);
123     if (injectedScript.hasNoValue())
124         return;
125
126     ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = ScriptDebugServer::DontPauseOnExceptions;
127     if (asBool(doNotPauseOnExceptionsAndMuteConsole))
128         previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions);
129     if (asBool(doNotPauseOnExceptionsAndMuteConsole))
130         muteConsole();
131
132     injectedScript.evaluate(errorString, expression, objectGroup ? *objectGroup : "", asBool(includeCommandLineAPI), asBool(returnByValue), asBool(generatePreview), &result, wasThrown);
133
134     if (asBool(doNotPauseOnExceptionsAndMuteConsole)) {
135         unmuteConsole();
136         setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState);
137     }
138 }
139
140 void InspectorRuntimeAgent::callFunctionOn(ErrorString& errorString, const String& objectId, const String& expression, const RefPtr<InspectorArray>* const optionalArguments, const bool* const doNotPauseOnExceptionsAndMuteConsole, const bool* const returnByValue, const bool* generatePreview, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result, Inspector::Protocol::OptOutput<bool>* wasThrown)
141 {
142     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
143     if (injectedScript.hasNoValue()) {
144         errorString = ASCIILiteral("Inspected frame has gone");
145         return;
146     }
147
148     String arguments;
149     if (optionalArguments)
150         arguments = (*optionalArguments)->toJSONString();
151
152     ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = ScriptDebugServer::DontPauseOnExceptions;
153     if (asBool(doNotPauseOnExceptionsAndMuteConsole))
154         previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions);
155     if (asBool(doNotPauseOnExceptionsAndMuteConsole))
156         muteConsole();
157
158     injectedScript.callFunctionOn(errorString, objectId, expression, arguments, asBool(returnByValue), asBool(generatePreview), &result, wasThrown);
159
160     if (asBool(doNotPauseOnExceptionsAndMuteConsole)) {
161         unmuteConsole();
162         setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState);
163     }
164 }
165
166 void InspectorRuntimeAgent::getProperties(ErrorString& errorString, const String& objectId, const bool* const ownProperties, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::PropertyDescriptor>>& result, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::InternalPropertyDescriptor>>& internalProperties)
167 {
168     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
169     if (injectedScript.hasNoValue()) {
170         errorString = ASCIILiteral("Inspected frame has gone");
171         return;
172     }
173
174     ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions);
175     muteConsole();
176
177     injectedScript.getProperties(errorString, objectId, ownProperties ? *ownProperties : false, &result);
178     injectedScript.getInternalProperties(errorString, objectId, &internalProperties);
179
180     unmuteConsole();
181     setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState);
182 }
183
184 void InspectorRuntimeAgent::releaseObject(ErrorString&, const String& objectId)
185 {
186     InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId);
187     if (!injectedScript.hasNoValue())
188         injectedScript.releaseObject(objectId);
189 }
190
191 void InspectorRuntimeAgent::releaseObjectGroup(ErrorString&, const String& objectGroup)
192 {
193     m_injectedScriptManager->releaseObjectGroup(objectGroup);
194 }
195
196 void InspectorRuntimeAgent::run(ErrorString&)
197 {
198 }
199
200 void InspectorRuntimeAgent::getRuntimeTypesForVariablesAtOffsets(ErrorString& errorString, const RefPtr<Inspector::InspectorArray>& locations, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::TypeDescription>>& typeDescriptions)
201 {
202     static const bool verbose = false;
203     VM& vm = globalVM();
204     typeDescriptions = Inspector::Protocol::Array<Inspector::Protocol::Runtime::TypeDescription>::create();
205     if (!vm.typeProfiler()) {
206         errorString = ASCIILiteral("The VM does not currently have Type Information.");
207         return;
208     }
209
210     double start = currentTimeMS();
211     vm.typeProfilerLog()->processLogEntries(ASCIILiteral("User Query"));
212
213     for (size_t i = 0; i < locations->length(); i++) {
214         RefPtr<Inspector::InspectorValue> value = locations->get(i);
215         RefPtr<InspectorObject> location;
216         if (!value->asObject(location)) {
217             errorString = ASCIILiteral("Array of TypeLocation objects has an object that does not have type of TypeLocation.");
218             return;
219         }
220
221         int descriptor;
222         String sourceIDAsString;
223         int divot;
224         location->getInteger(ASCIILiteral("typeInformationDescriptor"), descriptor);
225         location->getString(ASCIILiteral("sourceID"), sourceIDAsString);
226         location->getInteger(ASCIILiteral("divot"), divot);
227
228         bool okay;
229         TypeLocation* typeLocation = vm.typeProfiler()->findLocation(divot, sourceIDAsString.toIntPtrStrict(&okay), static_cast<TypeProfilerSearchDescriptor>(descriptor));
230
231         RefPtr<TypeSet> typeSet;
232         if (typeLocation) {
233             if (typeLocation->m_globalTypeSet && typeLocation->m_globalVariableID != TypeProfilerNoGlobalIDExists)
234                 typeSet = typeLocation->m_globalTypeSet;
235             else
236                 typeSet = typeLocation->m_instructionTypeSet;
237         }
238
239         bool isValid = typeLocation && typeSet && !typeSet->isEmpty();
240         RefPtr<Inspector::Protocol::Runtime::TypeDescription> description = Inspector::Protocol::Runtime::TypeDescription::create()
241             .setIsValid(isValid);
242
243         if (isValid) {
244             description->setLeastCommonAncestor(typeSet->leastCommonAncestor());
245             description->setPrimitiveTypeNames(typeSet->allPrimitiveTypeNames());
246             description->setStructures(typeSet->allStructureRepresentations());
247             description->setTypeSet(typeSet->inspectorTypeSet());
248             description->setIsTruncated(typeSet->isOverflown());
249         }
250
251         typeDescriptions->addItem(description);
252     }
253
254     double end = currentTimeMS();
255     if (verbose)
256         dataLogF("Inspector::getRuntimeTypesForVariablesAtOffsets took %lfms\n", end - start);
257 }
258
259 class TypeRecompiler : public MarkedBlock::VoidFunctor {
260 public:
261     inline void operator()(JSCell* cell)
262     {
263         if (!cell->inherits(FunctionExecutable::info()))
264             return;
265
266         FunctionExecutable* executable = jsCast<FunctionExecutable*>(cell);
267         executable->clearCodeIfNotCompiling();
268         executable->clearUnlinkedCodeForRecompilationIfNotCompiling();
269     }
270 };
271
272 static void recompileAllJSFunctionsForTypeProfiling(VM& vm, bool shouldEnableTypeProfiling)
273 {
274     vm.prepareToDiscardCode();
275
276     bool needsToRecompile = (shouldEnableTypeProfiling ? vm.enableTypeProfiler() : vm.disableTypeProfiler());
277     if (needsToRecompile) {
278         TypeRecompiler recompiler;
279         HeapIterationScope iterationScope(vm.heap);
280         vm.heap.objectSpace().forEachLiveCell(iterationScope, recompiler);
281     }
282 }
283
284 void InspectorRuntimeAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason reason)
285 {
286     if (reason != InspectorDisconnectReason::InspectedTargetDestroyed && m_isTypeProfilingEnabled)
287         setTypeProfilerEnabledState(false);
288 }
289
290 void InspectorRuntimeAgent::enableTypeProfiler(ErrorString&)
291 {
292     setTypeProfilerEnabledState(true);
293 }
294
295 void InspectorRuntimeAgent::disableTypeProfiler(ErrorString&)
296 {
297     setTypeProfilerEnabledState(false);
298 }
299
300 void InspectorRuntimeAgent::setTypeProfilerEnabledState(bool shouldEnableTypeProfiling)
301 {
302     if (m_isTypeProfilingEnabled == shouldEnableTypeProfiling)
303         return;
304
305     m_isTypeProfilingEnabled = shouldEnableTypeProfiling;
306
307     VM& vm = globalVM();
308
309     // If JavaScript is running, it's not safe to recompile, since we'll end
310     // up throwing away code that is live on the stack.
311     if (vm.entryScope) {
312         vm.entryScope->setEntryScopeDidPopListener(this,
313             [=] (VM& vm, JSGlobalObject*) {
314                 recompileAllJSFunctionsForTypeProfiling(vm, shouldEnableTypeProfiling); 
315             }
316         );
317     } else
318         recompileAllJSFunctionsForTypeProfiling(vm, shouldEnableTypeProfiling);
319 }
320
321 } // namespace Inspector
322
323 #endif // ENABLE(INSPECTOR)