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