diff options
Diffstat (limited to 'Source/JavaScriptCore/inspector/agents')
18 files changed, 2551 insertions, 485 deletions
diff --git a/Source/JavaScriptCore/inspector/agents/InspectorAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorAgent.cpp index fcee46e9d..f64baf3d3 100644 --- a/Source/JavaScriptCore/inspector/agents/InspectorAgent.cpp +++ b/Source/JavaScriptCore/inspector/agents/InspectorAgent.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007-2010, 2015 Apple Inc. All rights reserved. * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> * Copyright (C) 2011 Google Inc. All rights reserved. * @@ -12,7 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -31,18 +31,18 @@ #include "config.h" #include "InspectorAgent.h" -#if ENABLE(INSPECTOR) - +#include "InspectorEnvironment.h" +#include "InspectorFrontendRouter.h" #include "InspectorValues.h" #include "ScriptValue.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> namespace Inspector { -InspectorAgent::InspectorAgent() +InspectorAgent::InspectorAgent(AgentContext& context) : InspectorAgentBase(ASCIILiteral("Inspector")) - , m_enabled(false) + , m_environment(context.environment) + , m_frontendDispatcher(std::make_unique<InspectorFrontendDispatcher>(context.frontendRouter)) + , m_backendDispatcher(InspectorBackendDispatcher::create(context.backendDispatcher, this)) { } @@ -50,43 +50,49 @@ InspectorAgent::~InspectorAgent() { } -void InspectorAgent::didCreateFrontendAndBackend(InspectorFrontendChannel* frontendChannel, InspectorBackendDispatcher* backendDispatcher) +void InspectorAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) { - m_frontendDispatcher = std::make_unique<InspectorInspectorFrontendDispatcher>(frontendChannel); - m_backendDispatcher = InspectorInspectorBackendDispatcher::create(backendDispatcher, this); } -void InspectorAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason) +void InspectorAgent::willDestroyFrontendAndBackend(DisconnectReason) { - m_frontendDispatcher = nullptr; - m_backendDispatcher.clear(); - m_pendingEvaluateTestCommands.clear(); - ErrorString error; - disable(&error); + ErrorString unused; + disable(unused); } -void InspectorAgent::enable(ErrorString*) +void InspectorAgent::enable(ErrorString&) { m_enabled = true; if (m_pendingInspectData.first) - inspect(m_pendingInspectData.first, m_pendingInspectData.second); + inspect(m_pendingInspectData.first.copyRef(), m_pendingInspectData.second.copyRef()); + +#if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS) + if (m_pendingExtraDomainsData) + m_frontendDispatcher->activateExtraDomains(m_pendingExtraDomainsData); +#endif + + for (auto& testCommand : m_pendingEvaluateTestCommands) + m_frontendDispatcher->evaluateForTestInFrontend(testCommand); - for (Vector<std::pair<long, String>>::iterator it = m_pendingEvaluateTestCommands.begin(); m_frontendDispatcher && it != m_pendingEvaluateTestCommands.end(); ++it) - m_frontendDispatcher->evaluateForTestInFrontend(static_cast<int>((*it).first), (*it).second); m_pendingEvaluateTestCommands.clear(); } -void InspectorAgent::disable(ErrorString*) +void InspectorAgent::disable(ErrorString&) { m_enabled = false; } -void InspectorAgent::inspect(PassRefPtr<TypeBuilder::Runtime::RemoteObject> objectToInspect, PassRefPtr<InspectorObject> hints) +void InspectorAgent::initialized(ErrorString&) { - if (m_enabled && m_frontendDispatcher) { + m_environment.frontendInitialized(); +} + +void InspectorAgent::inspect(RefPtr<Protocol::Runtime::RemoteObject>&& objectToInspect, RefPtr<InspectorObject>&& hints) +{ + if (m_enabled) { m_frontendDispatcher->inspect(objectToInspect, hints); m_pendingInspectData.first = nullptr; m_pendingInspectData.second = nullptr; @@ -97,14 +103,43 @@ void InspectorAgent::inspect(PassRefPtr<TypeBuilder::Runtime::RemoteObject> obje m_pendingInspectData.second = hints; } -void InspectorAgent::evaluateForTestInFrontend(long callId, const String& script) +void InspectorAgent::evaluateForTestInFrontend(const String& script) { - if (m_enabled && m_frontendDispatcher) - m_frontendDispatcher->evaluateForTestInFrontend(static_cast<int>(callId), script); + if (m_enabled) + m_frontendDispatcher->evaluateForTestInFrontend(script); else - m_pendingEvaluateTestCommands.append(std::pair<long, String>(callId, script)); + m_pendingEvaluateTestCommands.append(script); } -} // namespace Inspector +#if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS) +void InspectorAgent::activateExtraDomain(const String& domainName) +{ + if (!m_enabled) { + if (!m_pendingExtraDomainsData) + m_pendingExtraDomainsData = Inspector::Protocol::Array<String>::create(); + m_pendingExtraDomainsData->addItem(domainName); + return; + } -#endif // ENABLE(INSPECTOR) + auto domainNames = Inspector::Protocol::Array<String>::create(); + domainNames->addItem(domainName); + m_frontendDispatcher->activateExtraDomains(WTFMove(domainNames)); +} + +void InspectorAgent::activateExtraDomains(const Vector<String>& extraDomains) +{ + if (extraDomains.isEmpty()) + return; + + auto domainNames = Inspector::Protocol::Array<String>::create(); + for (auto domainName : extraDomains) + domainNames->addItem(domainName); + + if (!m_enabled) + m_pendingExtraDomainsData = WTFMove(domainNames); + else + m_frontendDispatcher->activateExtraDomains(WTFMove(domainNames)); +} +#endif + +} // namespace Inspector diff --git a/Source/JavaScriptCore/inspector/agents/InspectorAgent.h b/Source/JavaScriptCore/inspector/agents/InspectorAgent.h index c7dc9ba5a..a9b039207 100644 --- a/Source/JavaScriptCore/inspector/agents/InspectorAgent.h +++ b/Source/JavaScriptCore/inspector/agents/InspectorAgent.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007-2010, 2015 Apple Inc. All rights reserved. * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -27,46 +27,55 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef InspectorAgent_h -#define InspectorAgent_h +#pragma once -#include "InspectorJSBackendDispatchers.h" -#include "InspectorJSFrontendDispatchers.h" +#include "InspectorBackendDispatchers.h" +#include "InspectorFrontendDispatchers.h" #include "inspector/InspectorAgentBase.h" #include <wtf/Forward.h> -#include <wtf/PassOwnPtr.h> #include <wtf/Vector.h> namespace Inspector { +class BackendDispatcher; +class InspectorEnvironment; class InspectorObject; -class InstrumentingAgents; typedef String ErrorString; -class JS_EXPORT_PRIVATE InspectorAgent final : public InspectorAgentBase, public InspectorInspectorBackendDispatcherHandler { +class JS_EXPORT_PRIVATE InspectorAgent final : public InspectorAgentBase, public InspectorBackendDispatcherHandler { WTF_MAKE_NONCOPYABLE(InspectorAgent); + WTF_MAKE_FAST_ALLOCATED; public: - InspectorAgent(); + InspectorAgent(AgentContext&); virtual ~InspectorAgent(); - virtual void didCreateFrontendAndBackend(InspectorFrontendChannel*, InspectorBackendDispatcher*) override; - virtual void willDestroyFrontendAndBackend(InspectorDisconnectReason reason) override; + void didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) override; + void willDestroyFrontendAndBackend(DisconnectReason) override; - virtual void enable(ErrorString*) override; - virtual void disable(ErrorString*) override; + void enable(ErrorString&) override; + void disable(ErrorString&) override; + void initialized(ErrorString&) override; - void inspect(PassRefPtr<TypeBuilder::Runtime::RemoteObject> objectToInspect, PassRefPtr<InspectorObject> hints); - void evaluateForTestInFrontend(long testCallId, const String& script); + void inspect(RefPtr<Protocol::Runtime::RemoteObject>&& objectToInspect, RefPtr<InspectorObject>&& hints); + void evaluateForTestInFrontend(const String& script); + +#if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS) + void activateExtraDomain(const String&); + void activateExtraDomains(const Vector<String>&); +#endif private: - std::unique_ptr<InspectorInspectorFrontendDispatcher> m_frontendDispatcher; - RefPtr<InspectorInspectorBackendDispatcher> m_backendDispatcher; - Vector<std::pair<long, String>> m_pendingEvaluateTestCommands; - std::pair<RefPtr<TypeBuilder::Runtime::RemoteObject>, RefPtr<InspectorObject>> m_pendingInspectData; - bool m_enabled; + InspectorEnvironment& m_environment; + std::unique_ptr<InspectorFrontendDispatcher> m_frontendDispatcher; + Ref<InspectorBackendDispatcher> m_backendDispatcher; + + Vector<String> m_pendingEvaluateTestCommands; + std::pair<RefPtr<Protocol::Runtime::RemoteObject>, RefPtr<InspectorObject>> m_pendingInspectData; +#if ENABLE(INSPECTOR_ALTERNATE_DISPATCHERS) + RefPtr<Inspector::Protocol::Array<String>> m_pendingExtraDomainsData; +#endif + bool m_enabled { false }; }; } // namespace Inspector - -#endif // !defined(InspectorAgent_h) diff --git a/Source/JavaScriptCore/inspector/agents/InspectorConsoleAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorConsoleAgent.cpp new file mode 100644 index 000000000..d453dbbd8 --- /dev/null +++ b/Source/JavaScriptCore/inspector/agents/InspectorConsoleAgent.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2014, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InspectorConsoleAgent.h" + +#include "ConsoleMessage.h" +#include "InjectedScriptManager.h" +#include "InspectorFrontendRouter.h" +#include "InspectorHeapAgent.h" +#include "ScriptArguments.h" +#include "ScriptCallFrame.h" +#include "ScriptCallStack.h" +#include "ScriptCallStackFactory.h" +#include "ScriptObject.h" +#include <wtf/CurrentTime.h> +#include <wtf/text/WTFString.h> + +namespace Inspector { + +static const unsigned maximumConsoleMessages = 100; +static const int expireConsoleMessagesStep = 10; + +InspectorConsoleAgent::InspectorConsoleAgent(AgentContext& context, InspectorHeapAgent* heapAgent) + : InspectorAgentBase(ASCIILiteral("Console")) + , m_injectedScriptManager(context.injectedScriptManager) + , m_frontendDispatcher(std::make_unique<ConsoleFrontendDispatcher>(context.frontendRouter)) + , m_backendDispatcher(ConsoleBackendDispatcher::create(context.backendDispatcher, this)) + , m_heapAgent(heapAgent) +{ +} + +InspectorConsoleAgent::~InspectorConsoleAgent() +{ +} + +void InspectorConsoleAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) +{ +} + +void InspectorConsoleAgent::willDestroyFrontendAndBackend(DisconnectReason) +{ + String errorString; + disable(errorString); +} + +void InspectorConsoleAgent::discardValues() +{ + m_consoleMessages.clear(); +} + +void InspectorConsoleAgent::enable(ErrorString&) +{ + if (m_enabled) + return; + + m_enabled = true; + + if (m_expiredConsoleMessageCount) { + ConsoleMessage expiredMessage(MessageSource::Other, MessageType::Log, MessageLevel::Warning, String::format("%d console messages are not shown.", m_expiredConsoleMessageCount)); + expiredMessage.addToFrontend(*m_frontendDispatcher, m_injectedScriptManager, false); + } + + size_t messageCount = m_consoleMessages.size(); + for (size_t i = 0; i < messageCount; ++i) + m_consoleMessages[i]->addToFrontend(*m_frontendDispatcher, m_injectedScriptManager, false); +} + +void InspectorConsoleAgent::disable(ErrorString&) +{ + if (!m_enabled) + return; + + m_enabled = false; +} + +void InspectorConsoleAgent::clearMessages(ErrorString&) +{ + m_consoleMessages.clear(); + m_expiredConsoleMessageCount = 0; + m_previousMessage = nullptr; + + m_injectedScriptManager.releaseObjectGroup(ASCIILiteral("console")); + + if (m_enabled) + m_frontendDispatcher->messagesCleared(); +} + +void InspectorConsoleAgent::reset() +{ + ErrorString unused; + clearMessages(unused); + + m_times.clear(); + m_counts.clear(); +} + +void InspectorConsoleAgent::addMessageToConsole(std::unique_ptr<ConsoleMessage> message) +{ + if (!m_injectedScriptManager.inspectorEnvironment().developerExtrasEnabled()) + return; + + if (message->type() == MessageType::Clear) { + ErrorString unused; + clearMessages(unused); + } + + addConsoleMessage(WTFMove(message)); +} + +void InspectorConsoleAgent::startTiming(const String& title) +{ + ASSERT(!title.isNull()); + if (title.isNull()) + return; + + auto result = m_times.add(title, monotonicallyIncreasingTime()); + + if (!result.isNewEntry) { + // FIXME: Send an enum to the frontend for localization? + String warning = makeString("Timer \"", title, "\" already exists"); + addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Timing, MessageLevel::Warning, warning)); + } +} + +void InspectorConsoleAgent::stopTiming(const String& title, Ref<ScriptCallStack>&& callStack) +{ + ASSERT(!title.isNull()); + if (title.isNull()) + return; + + auto it = m_times.find(title); + if (it == m_times.end()) { + // FIXME: Send an enum to the frontend for localization? + String warning = makeString("Timer \"", title, "\" does not exist"); + addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Timing, MessageLevel::Warning, warning)); + return; + } + + double startTime = it->value; + m_times.remove(it); + + double elapsed = monotonicallyIncreasingTime() - startTime; + String message = title + String::format(": %.3fms", elapsed * 1000); + addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Timing, MessageLevel::Debug, message, WTFMove(callStack))); +} + +void InspectorConsoleAgent::takeHeapSnapshot(const String& title) +{ + if (!m_injectedScriptManager.inspectorEnvironment().developerExtrasEnabled()) + return; + + ErrorString ignored; + double timestamp; + String snapshotData; + m_heapAgent->snapshot(ignored, ×tamp, &snapshotData); + + m_frontendDispatcher->heapSnapshot(timestamp, snapshotData, title.isEmpty() ? nullptr : &title); +} + +void InspectorConsoleAgent::count(JSC::ExecState* state, Ref<ScriptArguments>&& arguments) +{ + Ref<ScriptCallStack> callStack = createScriptCallStackForConsole(state, ScriptCallStack::maxCallStackSizeToCapture); + + String title; + String identifier; + if (!arguments->argumentCount()) { + // '@' prefix for engine generated labels. + title = ASCIILiteral("Global"); + identifier = makeString('@', title); + } else { + // '#' prefix for user labels. + arguments->getFirstArgumentAsString(title); + identifier = makeString('#', title); + } + + auto result = m_counts.add(identifier, 1); + if (!result.isNewEntry) + result.iterator->value += 1; + + // FIXME: Web Inspector should have a better UI for counters, but for now we just log an updated counter value. + + String message = makeString(title, ": ", String::number(result.iterator->value)); + addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::ConsoleAPI, MessageType::Log, MessageLevel::Debug, message, WTFMove(callStack))); +} + +static bool isGroupMessage(MessageType type) +{ + return type == MessageType::StartGroup + || type == MessageType::StartGroupCollapsed + || type == MessageType::EndGroup; +} + +void InspectorConsoleAgent::addConsoleMessage(std::unique_ptr<ConsoleMessage> consoleMessage) +{ + ASSERT(m_injectedScriptManager.inspectorEnvironment().developerExtrasEnabled()); + ASSERT_ARG(consoleMessage, consoleMessage); + + if (m_previousMessage && !isGroupMessage(m_previousMessage->type()) && m_previousMessage->isEqual(consoleMessage.get())) { + m_previousMessage->incrementCount(); + if (m_enabled) + m_previousMessage->updateRepeatCountInConsole(*m_frontendDispatcher); + } else { + m_previousMessage = consoleMessage.get(); + m_consoleMessages.append(WTFMove(consoleMessage)); + if (m_enabled) + m_previousMessage->addToFrontend(*m_frontendDispatcher, m_injectedScriptManager, true); + } + + if (m_consoleMessages.size() >= maximumConsoleMessages) { + m_expiredConsoleMessageCount += expireConsoleMessagesStep; + m_consoleMessages.remove(0, expireConsoleMessagesStep); + } +} + +} // namespace Inspector diff --git a/Source/JavaScriptCore/inspector/agents/InspectorConsoleAgent.h b/Source/JavaScriptCore/inspector/agents/InspectorConsoleAgent.h new file mode 100644 index 000000000..175cec9da --- /dev/null +++ b/Source/JavaScriptCore/inspector/agents/InspectorConsoleAgent.h @@ -0,0 +1,94 @@ +/* +* Copyright (C) 2014, 2015 Apple Inc. All rights reserved. +* Copyright (C) 2011 Google Inc. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#include "InspectorBackendDispatchers.h" +#include "InspectorFrontendDispatchers.h" +#include "inspector/InspectorAgentBase.h" +#include "runtime/ConsoleTypes.h" +#include <wtf/Forward.h> +#include <wtf/HashMap.h> +#include <wtf/Noncopyable.h> +#include <wtf/Vector.h> +#include <wtf/text/StringHash.h> + +namespace JSC { +class ExecState; +} + +namespace Inspector { + +class ConsoleMessage; +class InjectedScriptManager; +class InspectorHeapAgent; +class ScriptArguments; +class ScriptCallStack; +typedef String ErrorString; + +class JS_EXPORT_PRIVATE InspectorConsoleAgent : public InspectorAgentBase, public ConsoleBackendDispatcherHandler { + WTF_MAKE_NONCOPYABLE(InspectorConsoleAgent); + WTF_MAKE_FAST_ALLOCATED; +public: + InspectorConsoleAgent(AgentContext&, InspectorHeapAgent*); + virtual ~InspectorConsoleAgent(); + + void didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) override; + void willDestroyFrontendAndBackend(DisconnectReason) override; + void discardValues() override; + + void enable(ErrorString&) override; + void disable(ErrorString&) override; + void clearMessages(ErrorString&) override; + void setMonitoringXHREnabled(ErrorString&, bool enabled) override = 0; + void addInspectedNode(ErrorString&, int nodeId) override = 0; + + bool enabled() const { return m_enabled; } + void reset(); + + void addMessageToConsole(std::unique_ptr<ConsoleMessage>); + + void startTiming(const String& title); + void stopTiming(const String& title, Ref<ScriptCallStack>&&); + void takeHeapSnapshot(const String& title); + void count(JSC::ExecState*, Ref<ScriptArguments>&&); + +protected: + void addConsoleMessage(std::unique_ptr<ConsoleMessage>); + + InjectedScriptManager& m_injectedScriptManager; + std::unique_ptr<ConsoleFrontendDispatcher> m_frontendDispatcher; + RefPtr<ConsoleBackendDispatcher> m_backendDispatcher; + InspectorHeapAgent* m_heapAgent; + + ConsoleMessage* m_previousMessage { nullptr }; + Vector<std::unique_ptr<ConsoleMessage>> m_consoleMessages; + int m_expiredConsoleMessageCount { 0 }; + HashMap<String, unsigned> m_counts; + HashMap<String, double> m_times; + bool m_enabled { false }; +}; + +} // namespace Inspector diff --git a/Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.cpp index 06b759568..617efb9ac 100644 --- a/Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.cpp +++ b/Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.cpp @@ -1,6 +1,6 @@ /* - * Copyright (C) 2010, 2013 Apple Inc. All rights reserved. - * Copyright (C) 2010-2011 Google Inc. All rights reserved. + * Copyright (C) 2010, 2013, 2015 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -30,38 +30,43 @@ #include "config.h" #include "InspectorDebuggerAgent.h" -#if ENABLE(INSPECTOR) - +#include "AsyncStackTrace.h" #include "ContentSearchUtilities.h" #include "InjectedScript.h" #include "InjectedScriptManager.h" +#include "InspectorFrontendRouter.h" #include "InspectorValues.h" +#include "JSCInlines.h" #include "RegularExpression.h" +#include "ScriptCallStackFactory.h" #include "ScriptDebugServer.h" #include "ScriptObject.h" #include "ScriptValue.h" +#include <wtf/NeverDestroyed.h> +#include <wtf/Stopwatch.h> #include <wtf/text/WTFString.h> namespace Inspector { const char* InspectorDebuggerAgent::backtraceObjectGroup = "backtrace"; -static String objectGroupForBreakpointAction(int identifier) +// Objects created and retained by evaluating breakpoint actions are put into object groups +// according to the breakpoint action identifier assigned by the frontend. A breakpoint may +// have several object groups, and objects from several backend breakpoint action instances may +// create objects in the same group. +static String objectGroupForBreakpointAction(const ScriptBreakpointAction& action) { - DEFINE_STATIC_LOCAL(const AtomicString, objectGroup, ("breakpoint-action-", AtomicString::ConstructFromLiteral)); - return makeString(objectGroup, String::number(identifier)); + static NeverDestroyed<String> objectGroup(ASCIILiteral("breakpoint-action-")); + return makeString(objectGroup.get(), String::number(action.identifier)); } -InspectorDebuggerAgent::InspectorDebuggerAgent(InjectedScriptManager* injectedScriptManager) +InspectorDebuggerAgent::InspectorDebuggerAgent(AgentContext& context) : InspectorAgentBase(ASCIILiteral("Debugger")) - , m_injectedScriptManager(injectedScriptManager) - , m_listener(nullptr) - , m_pausedScriptState(nullptr) + , m_injectedScriptManager(context.injectedScriptManager) + , m_frontendDispatcher(std::make_unique<DebuggerFrontendDispatcher>(context.frontendRouter)) + , m_backendDispatcher(DebuggerBackendDispatcher::create(context.backendDispatcher, this)) + , m_scriptDebugServer(context.environment.scriptDebugServer()) , m_continueToLocationBreakpointID(JSC::noBreakpointID) - , m_enabled(false) - , m_javaScriptPauseScheduled(false) - , m_nextProbeSampleId(1) - , m_nextBreakpointActionIdentifier(1) { // FIXME: make breakReason optional so that there was no need to init it with "other". clearBreakDetails(); @@ -71,18 +76,13 @@ InspectorDebuggerAgent::~InspectorDebuggerAgent() { } -void InspectorDebuggerAgent::didCreateFrontendAndBackend(InspectorFrontendChannel* frontendChannel, InspectorBackendDispatcher* backendDispatcher) +void InspectorDebuggerAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) { - m_frontendDispatcher = std::make_unique<InspectorDebuggerFrontendDispatcher>(frontendChannel); - m_backendDispatcher = InspectorDebuggerBackendDispatcher::create(backendDispatcher, this); } -void InspectorDebuggerAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason reason) +void InspectorDebuggerAgent::willDestroyFrontendAndBackend(DisconnectReason reason) { - m_frontendDispatcher = nullptr; - m_backendDispatcher.clear(); - - bool skipRecompile = reason == InspectorDisconnectReason::InspectedTargetDestroyed; + bool skipRecompile = reason == DisconnectReason::InspectedTargetDestroyed; disable(skipRecompile); } @@ -91,8 +91,7 @@ void InspectorDebuggerAgent::enable() if (m_enabled) return; - scriptDebugServer().setBreakpointsActivated(true); - startListeningScriptDebugServer(); + m_scriptDebugServer.addListener(this); if (m_listener) m_listener->debuggerWasEnabled(); @@ -105,55 +104,221 @@ void InspectorDebuggerAgent::disable(bool isBeingDestroyed) if (!m_enabled) return; - m_javaScriptBreakpoints.clear(); + m_scriptDebugServer.removeListener(this, isBeingDestroyed); + clearInspectorBreakpointState(); + + if (!isBeingDestroyed) + m_scriptDebugServer.deactivateBreakpoints(); - stopListeningScriptDebugServer(isBeingDestroyed); - clearResolvedBreakpointState(); + ASSERT(m_javaScriptBreakpoints.isEmpty()); if (m_listener) m_listener->debuggerWasDisabled(); + clearAsyncStackTraceData(); + + m_pauseOnAssertionFailures = false; + m_enabled = false; } -void InspectorDebuggerAgent::enable(ErrorString*) +void InspectorDebuggerAgent::enable(ErrorString&) { enable(); } -void InspectorDebuggerAgent::disable(ErrorString*) +void InspectorDebuggerAgent::disable(ErrorString&) { disable(false); } -void InspectorDebuggerAgent::setBreakpointsActive(ErrorString*, bool active) +bool InspectorDebuggerAgent::breakpointsActive() const +{ + return m_scriptDebugServer.breakpointsActive(); +} + +void InspectorDebuggerAgent::setAsyncStackTraceDepth(ErrorString& errorString, int depth) +{ + if (m_asyncStackTraceDepth == depth) + return; + + if (depth < 0) { + errorString = ASCIILiteral("depth must be a positive number."); + return; + } + + m_asyncStackTraceDepth = depth; + + if (!m_asyncStackTraceDepth) + clearAsyncStackTraceData(); +} + +void InspectorDebuggerAgent::setBreakpointsActive(ErrorString&, bool active) { if (active) - scriptDebugServer().activateBreakpoints(); + m_scriptDebugServer.activateBreakpoints(); else - scriptDebugServer().deactivateBreakpoints(); + m_scriptDebugServer.deactivateBreakpoints(); +} + +bool InspectorDebuggerAgent::isPaused() const +{ + return m_scriptDebugServer.isPaused(); +} + +void InspectorDebuggerAgent::setSuppressAllPauses(bool suppress) +{ + m_scriptDebugServer.setSuppressAllPauses(suppress); +} + +static RefPtr<InspectorObject> buildAssertPauseReason(const String& message) +{ + auto reason = Inspector::Protocol::Debugger::AssertPauseReason::create().release(); + if (!message.isNull()) + reason->setMessage(message); + return reason->openAccessors(); +} + +static RefPtr<InspectorObject> buildCSPViolationPauseReason(const String& directiveText) +{ + auto reason = Inspector::Protocol::Debugger::CSPViolationPauseReason::create() + .setDirective(directiveText) + .release(); + return reason->openAccessors(); +} + +RefPtr<InspectorObject> InspectorDebuggerAgent::buildBreakpointPauseReason(JSC::BreakpointID debuggerBreakpointIdentifier) +{ + ASSERT(debuggerBreakpointIdentifier != JSC::noBreakpointID); + auto it = m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.find(debuggerBreakpointIdentifier); + if (it == m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.end()) + return nullptr; + + auto reason = Inspector::Protocol::Debugger::BreakpointPauseReason::create() + .setBreakpointId(it->value) + .release(); + return reason->openAccessors(); +} + +RefPtr<InspectorObject> InspectorDebuggerAgent::buildExceptionPauseReason(JSC::JSValue exception, const InjectedScript& injectedScript) +{ + ASSERT(exception); + if (!exception) + return nullptr; + + ASSERT(!injectedScript.hasNoValue()); + if (injectedScript.hasNoValue()) + return nullptr; + + return injectedScript.wrapObject(exception, InspectorDebuggerAgent::backtraceObjectGroup)->openAccessors(); +} + +void InspectorDebuggerAgent::handleConsoleAssert(const String& message) +{ + if (!m_scriptDebugServer.breakpointsActive()) + return; + + if (m_pauseOnAssertionFailures) + breakProgram(DebuggerFrontendDispatcher::Reason::Assert, buildAssertPauseReason(message)); +} + +void InspectorDebuggerAgent::didScheduleAsyncCall(JSC::ExecState* exec, int asyncCallType, int callbackIdentifier, bool singleShot) +{ + if (!m_asyncStackTraceDepth) + return; + + if (!m_scriptDebugServer.breakpointsActive()) + return; + + Ref<ScriptCallStack> callStack = createScriptCallStack(exec, m_asyncStackTraceDepth); + ASSERT(callStack->size()); + if (!callStack->size()) + return; + + RefPtr<AsyncStackTrace> parentStackTrace; + if (m_currentAsyncCallIdentifier) { + auto it = m_pendingAsyncCalls.find(m_currentAsyncCallIdentifier.value()); + ASSERT(it != m_pendingAsyncCalls.end()); + parentStackTrace = it->value; + } + + auto identifier = std::make_pair(asyncCallType, callbackIdentifier); + auto asyncStackTrace = AsyncStackTrace::create(WTFMove(callStack), singleShot, WTFMove(parentStackTrace)); + + m_pendingAsyncCalls.set(identifier, WTFMove(asyncStackTrace)); +} + +void InspectorDebuggerAgent::didCancelAsyncCall(int asyncCallType, int callbackIdentifier) +{ + if (!m_asyncStackTraceDepth) + return; + + auto identifier = std::make_pair(asyncCallType, callbackIdentifier); + auto it = m_pendingAsyncCalls.find(identifier); + if (it == m_pendingAsyncCalls.end()) + return; + + auto& asyncStackTrace = it->value; + asyncStackTrace->didCancelAsyncCall(); + + if (m_currentAsyncCallIdentifier && m_currentAsyncCallIdentifier.value() == identifier) + return; + + m_pendingAsyncCalls.remove(identifier); } -bool InspectorDebuggerAgent::isPaused() +void InspectorDebuggerAgent::willDispatchAsyncCall(int asyncCallType, int callbackIdentifier) { - return scriptDebugServer().isPaused(); + if (!m_asyncStackTraceDepth) + return; + + if (m_currentAsyncCallIdentifier) + return; + + // A call can be scheduled before the Inspector is opened, or while async stack + // traces are disabled. If no call data exists, do nothing. + auto identifier = std::make_pair(asyncCallType, callbackIdentifier); + auto it = m_pendingAsyncCalls.find(identifier); + if (it == m_pendingAsyncCalls.end()) + return; + + auto& asyncStackTrace = it->value; + asyncStackTrace->willDispatchAsyncCall(m_asyncStackTraceDepth); + + m_currentAsyncCallIdentifier = identifier; } -void InspectorDebuggerAgent::handleConsoleAssert() +void InspectorDebuggerAgent::didDispatchAsyncCall() { - if (scriptDebugServer().pauseOnExceptionsState() != JSC::Debugger::DontPauseOnExceptions) - breakProgram(InspectorDebuggerFrontendDispatcher::Reason::Assert, nullptr); + if (!m_asyncStackTraceDepth) + return; + + if (!m_currentAsyncCallIdentifier) + return; + + auto identifier = m_currentAsyncCallIdentifier.value(); + auto it = m_pendingAsyncCalls.find(identifier); + ASSERT(it != m_pendingAsyncCalls.end()); + + auto& asyncStackTrace = it->value; + asyncStackTrace->didDispatchAsyncCall(); + + m_currentAsyncCallIdentifier = std::nullopt; + + if (!asyncStackTrace->isPending()) + m_pendingAsyncCalls.remove(identifier); } -static PassRefPtr<InspectorObject> buildObjectForBreakpointCookie(const String& url, int lineNumber, int columnNumber, const String& condition, RefPtr<InspectorArray>& actions, bool isRegex, bool autoContinue) +static Ref<InspectorObject> buildObjectForBreakpointCookie(const String& url, int lineNumber, int columnNumber, const String& condition, RefPtr<InspectorArray>& actions, bool isRegex, bool autoContinue, unsigned ignoreCount) { - RefPtr<InspectorObject> breakpointObject = InspectorObject::create(); + Ref<InspectorObject> breakpointObject = InspectorObject::create(); breakpointObject->setString(ASCIILiteral("url"), url); - breakpointObject->setNumber(ASCIILiteral("lineNumber"), lineNumber); - breakpointObject->setNumber(ASCIILiteral("columnNumber"), columnNumber); + breakpointObject->setInteger(ASCIILiteral("lineNumber"), lineNumber); + breakpointObject->setInteger(ASCIILiteral("columnNumber"), columnNumber); breakpointObject->setString(ASCIILiteral("condition"), condition); breakpointObject->setBoolean(ASCIILiteral("isRegex"), isRegex); breakpointObject->setBoolean(ASCIILiteral("autoContinue"), autoContinue); + breakpointObject->setInteger(ASCIILiteral("ignoreCount"), ignoreCount); if (actions) breakpointObject->setArray(ASCIILiteral("actions"), actions); @@ -172,19 +337,19 @@ static bool matches(const String& url, const String& pattern, bool isRegex) static bool breakpointActionTypeForString(const String& typeString, ScriptBreakpointActionType* output) { - if (typeString == Inspector::TypeBuilder::getJSEnumConstantValue(Inspector::TypeBuilder::Debugger::BreakpointAction::Type::Log)) { + if (typeString == Inspector::Protocol::InspectorHelpers::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Log)) { *output = ScriptBreakpointActionTypeLog; return true; } - if (typeString == Inspector::TypeBuilder::getJSEnumConstantValue(Inspector::TypeBuilder::Debugger::BreakpointAction::Type::Evaluate)) { + if (typeString == Inspector::Protocol::InspectorHelpers::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Evaluate)) { *output = ScriptBreakpointActionTypeEvaluate; return true; } - if (typeString == Inspector::TypeBuilder::getJSEnumConstantValue(Inspector::TypeBuilder::Debugger::BreakpointAction::Type::Sound)) { + if (typeString == Inspector::Protocol::InspectorHelpers::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Sound)) { *output = ScriptBreakpointActionTypeSound; return true; } - if (typeString == Inspector::TypeBuilder::getJSEnumConstantValue(Inspector::TypeBuilder::Debugger::BreakpointAction::Type::Probe)) { + if (typeString == Inspector::Protocol::InspectorHelpers::getEnumConstantValue(Inspector::Protocol::Debugger::BreakpointAction::Type::Probe)) { *output = ScriptBreakpointActionTypeProbe; return true; } @@ -192,7 +357,7 @@ static bool breakpointActionTypeForString(const String& typeString, ScriptBreakp return false; } -bool InspectorDebuggerAgent::breakpointActionsFromProtocol(ErrorString* errorString, RefPtr<InspectorArray>& actions, Vector<ScriptBreakpointAction>* result) +bool InspectorDebuggerAgent::breakpointActionsFromProtocol(ErrorString& errorString, RefPtr<InspectorArray>& actions, BreakpointActions* result) { if (!actions) return true; @@ -205,37 +370,70 @@ bool InspectorDebuggerAgent::breakpointActionsFromProtocol(ErrorString* errorStr for (unsigned i = 0; i < actionsLength; ++i) { RefPtr<InspectorValue> value = actions->get(i); RefPtr<InspectorObject> object; - if (!value->asObject(&object)) { - *errorString = ASCIILiteral("BreakpointAction of incorrect type, expected object"); + if (!value->asObject(object)) { + errorString = ASCIILiteral("BreakpointAction of incorrect type, expected object"); return false; } String typeString; - if (!object->getString(ASCIILiteral("type"), &typeString)) { - *errorString = ASCIILiteral("BreakpointAction had type missing"); + if (!object->getString(ASCIILiteral("type"), typeString)) { + errorString = ASCIILiteral("BreakpointAction had type missing"); return false; } ScriptBreakpointActionType type; if (!breakpointActionTypeForString(typeString, &type)) { - *errorString = ASCIILiteral("BreakpointAction had unknown type"); + errorString = ASCIILiteral("BreakpointAction had unknown type"); return false; } + // Specifying an identifier is optional. They are used to correlate probe samples + // in the frontend across multiple backend probe actions and segregate object groups. + int identifier = 0; + object->getInteger(ASCIILiteral("id"), identifier); + String data; - object->getString(ASCIILiteral("data"), &data); + object->getString(ASCIILiteral("data"), data); - result->append(ScriptBreakpointAction(type, m_nextBreakpointActionIdentifier++, data)); + result->append(ScriptBreakpointAction(type, identifier, data)); } return true; } -void InspectorDebuggerAgent::setBreakpointByUrl(ErrorString* errorString, int lineNumber, const String* const optionalURL, const String* const optionalURLRegex, const int* const optionalColumnNumber, const RefPtr<InspectorObject>* options, Inspector::TypeBuilder::Debugger::BreakpointId* outBreakpointIdentifier, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Debugger::Location>>& locations, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Debugger::BreakpointActionIdentifier>>& breakpointActionIdentifiers) +static RefPtr<Inspector::Protocol::Debugger::Location> buildDebuggerLocation(const JSC::Breakpoint& breakpoint) +{ + ASSERT(breakpoint.resolved); + + auto location = Inspector::Protocol::Debugger::Location::create() + .setScriptId(String::number(breakpoint.sourceID)) + .setLineNumber(breakpoint.line) + .release(); + location->setColumnNumber(breakpoint.column); + + return WTFMove(location); +} + +static bool parseLocation(ErrorString& errorString, const InspectorObject& location, JSC::SourceID& sourceID, unsigned& lineNumber, unsigned& columnNumber) { - locations = Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Debugger::Location>::create(); + String scriptIDStr; + if (!location.getString(ASCIILiteral("scriptId"), scriptIDStr) || !location.getInteger(ASCIILiteral("lineNumber"), lineNumber)) { + sourceID = JSC::noSourceID; + errorString = ASCIILiteral("scriptId and lineNumber are required."); + return false; + } + + sourceID = scriptIDStr.toIntPtr(); + columnNumber = 0; + location.getInteger(ASCIILiteral("columnNumber"), columnNumber); + return true; +} + +void InspectorDebuggerAgent::setBreakpointByUrl(ErrorString& errorString, int lineNumber, const String* const optionalURL, const String* const optionalURLRegex, const int* const optionalColumnNumber, const InspectorObject* options, Inspector::Protocol::Debugger::BreakpointId* outBreakpointIdentifier, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Debugger::Location>>& locations) +{ + locations = Inspector::Protocol::Array<Inspector::Protocol::Debugger::Location>::create(); if (!optionalURL == !optionalURLRegex) { - *errorString = ASCIILiteral("Either url or urlRegex must be specified."); + errorString = ASCIILiteral("Either url or urlRegex must be specified."); return; } @@ -245,258 +443,361 @@ void InspectorDebuggerAgent::setBreakpointByUrl(ErrorString* errorString, int li String breakpointIdentifier = (isRegex ? "/" + url + "/" : url) + ':' + String::number(lineNumber) + ':' + String::number(columnNumber); if (m_javaScriptBreakpoints.contains(breakpointIdentifier)) { - *errorString = ASCIILiteral("Breakpoint at specified location already exists."); + errorString = ASCIILiteral("Breakpoint at specified location already exists."); return; } String condition = emptyString(); bool autoContinue = false; + unsigned ignoreCount = 0; RefPtr<InspectorArray> actions; if (options) { - (*options)->getString(ASCIILiteral("condition"), &condition); - (*options)->getBoolean(ASCIILiteral("autoContinue"), &autoContinue); - actions = (*options)->getArray(ASCIILiteral("actions")); + options->getString(ASCIILiteral("condition"), condition); + options->getBoolean(ASCIILiteral("autoContinue"), autoContinue); + options->getArray(ASCIILiteral("actions"), actions); + options->getInteger(ASCIILiteral("ignoreCount"), ignoreCount); } - Vector<ScriptBreakpointAction> breakpointActions; + BreakpointActions breakpointActions; if (!breakpointActionsFromProtocol(errorString, actions, &breakpointActions)) return; - breakpointActionIdentifiers = Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Debugger::BreakpointActionIdentifier>::create(); - for (ScriptBreakpointAction& action : breakpointActions) - breakpointActionIdentifiers->addItem(action.identifier); + m_javaScriptBreakpoints.set(breakpointIdentifier, buildObjectForBreakpointCookie(url, lineNumber, columnNumber, condition, actions, isRegex, autoContinue, ignoreCount)); - m_javaScriptBreakpoints.set(breakpointIdentifier, buildObjectForBreakpointCookie(url, lineNumber, columnNumber, condition, actions, isRegex, autoContinue)); + for (auto& entry : m_scripts) { + Script& script = entry.value; + String scriptURLForBreakpoints = !script.sourceURL.isEmpty() ? script.sourceURL : script.url; + if (!matches(scriptURLForBreakpoints, url, isRegex)) + continue; - ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition, breakpointActions, autoContinue); - for (ScriptsMap::iterator it = m_scripts.begin(); it != m_scripts.end(); ++it) { - String scriptURL = !it->value.sourceURL.isEmpty() ? it->value.sourceURL : it->value.url; - if (!matches(scriptURL, url, isRegex)) + JSC::SourceID sourceID = entry.key; + JSC::Breakpoint breakpoint(sourceID, lineNumber, columnNumber, condition, autoContinue, ignoreCount); + resolveBreakpoint(script, breakpoint); + if (!breakpoint.resolved) continue; - RefPtr<Inspector::TypeBuilder::Debugger::Location> location = resolveBreakpoint(breakpointIdentifier, it->key, breakpoint); - if (location) - locations->addItem(location); - } - *outBreakpointIdentifier = breakpointIdentifier; -} + bool existing; + setBreakpoint(breakpoint, existing); + if (existing) + continue; -static bool parseLocation(ErrorString* errorString, InspectorObject* location, JSC::SourceID* sourceID, unsigned* lineNumber, unsigned* columnNumber) -{ - String scriptIDStr; - if (!location->getString(ASCIILiteral("scriptId"), &scriptIDStr) || !location->getNumber(ASCIILiteral("lineNumber"), lineNumber)) { - *sourceID = JSC::noSourceID; - *errorString = ASCIILiteral("scriptId and lineNumber are required."); - return false; + ScriptBreakpoint scriptBreakpoint(breakpoint.line, breakpoint.column, condition, breakpointActions, autoContinue, ignoreCount); + didSetBreakpoint(breakpoint, breakpointIdentifier, scriptBreakpoint); + + locations->addItem(buildDebuggerLocation(breakpoint)); } - *sourceID = scriptIDStr.toIntPtr(); - *columnNumber = 0; - location->getNumber(ASCIILiteral("columnNumber"), columnNumber); - return true; + *outBreakpointIdentifier = breakpointIdentifier; } -void InspectorDebuggerAgent::setBreakpoint(ErrorString* errorString, const RefPtr<InspectorObject>& location, const RefPtr<InspectorObject>* options, Inspector::TypeBuilder::Debugger::BreakpointId* outBreakpointIdentifier, RefPtr<Inspector::TypeBuilder::Debugger::Location>& actualLocation, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Debugger::BreakpointActionIdentifier>>& breakpointActionIdentifiers) +void InspectorDebuggerAgent::setBreakpoint(ErrorString& errorString, const InspectorObject& location, const InspectorObject* options, Inspector::Protocol::Debugger::BreakpointId* outBreakpointIdentifier, RefPtr<Inspector::Protocol::Debugger::Location>& actualLocation) { JSC::SourceID sourceID; unsigned lineNumber; unsigned columnNumber; - if (!parseLocation(errorString, location.get(), &sourceID, &lineNumber, &columnNumber)) + if (!parseLocation(errorString, location, sourceID, lineNumber, columnNumber)) return; String condition = emptyString(); bool autoContinue = false; + unsigned ignoreCount = 0; RefPtr<InspectorArray> actions; if (options) { - (*options)->getString(ASCIILiteral("condition"), &condition); - (*options)->getBoolean(ASCIILiteral("autoContinue"), &autoContinue); - actions = (*options)->getArray(ASCIILiteral("actions")); + options->getString(ASCIILiteral("condition"), condition); + options->getBoolean(ASCIILiteral("autoContinue"), autoContinue); + options->getArray(ASCIILiteral("actions"), actions); + options->getInteger(ASCIILiteral("ignoreCount"), ignoreCount); } - Vector<ScriptBreakpointAction> breakpointActions; + BreakpointActions breakpointActions; if (!breakpointActionsFromProtocol(errorString, actions, &breakpointActions)) return; - breakpointActionIdentifiers = Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Debugger::BreakpointActionIdentifier>::create(); - for (ScriptBreakpointAction& action : breakpointActions) - breakpointActionIdentifiers->addItem(action.identifier); + auto scriptIterator = m_scripts.find(sourceID); + if (scriptIterator == m_scripts.end()) { + errorString = ASCIILiteral("No script for id: ") + String::number(sourceID); + return; + } - String breakpointIdentifier = String::number(sourceID) + ':' + String::number(lineNumber) + ':' + String::number(columnNumber); - if (m_breakpointIdentifierToDebugServerBreakpointIDs.find(breakpointIdentifier) != m_breakpointIdentifierToDebugServerBreakpointIDs.end()) { - *errorString = ASCIILiteral("Breakpoint at specified location already exists."); + Script& script = scriptIterator->value; + JSC::Breakpoint breakpoint(sourceID, lineNumber, columnNumber, condition, autoContinue, ignoreCount); + resolveBreakpoint(script, breakpoint); + if (!breakpoint.resolved) { + errorString = ASCIILiteral("Could not resolve breakpoint"); return; } - ScriptBreakpoint breakpoint(lineNumber, columnNumber, condition, breakpointActions, autoContinue); - actualLocation = resolveBreakpoint(breakpointIdentifier, sourceID, breakpoint); - if (!actualLocation) { - *errorString = ASCIILiteral("Could not resolve breakpoint"); + bool existing; + setBreakpoint(breakpoint, existing); + if (existing) { + errorString = ASCIILiteral("Breakpoint at specified location already exists"); return; } + String breakpointIdentifier = String::number(sourceID) + ':' + String::number(breakpoint.line) + ':' + String::number(breakpoint.column); + ScriptBreakpoint scriptBreakpoint(breakpoint.line, breakpoint.column, condition, breakpointActions, autoContinue, ignoreCount); + didSetBreakpoint(breakpoint, breakpointIdentifier, scriptBreakpoint); + + actualLocation = buildDebuggerLocation(breakpoint); *outBreakpointIdentifier = breakpointIdentifier; } -void InspectorDebuggerAgent::removeBreakpoint(ErrorString*, const String& breakpointIdentifier) +void InspectorDebuggerAgent::didSetBreakpoint(const JSC::Breakpoint& breakpoint, const String& breakpointIdentifier, const ScriptBreakpoint& scriptBreakpoint) +{ + JSC::BreakpointID id = breakpoint.id; + m_scriptDebugServer.setBreakpointActions(id, scriptBreakpoint); + + auto debugServerBreakpointIDsIterator = m_breakpointIdentifierToDebugServerBreakpointIDs.find(breakpointIdentifier); + if (debugServerBreakpointIDsIterator == m_breakpointIdentifierToDebugServerBreakpointIDs.end()) + debugServerBreakpointIDsIterator = m_breakpointIdentifierToDebugServerBreakpointIDs.set(breakpointIdentifier, Vector<JSC::BreakpointID>()).iterator; + debugServerBreakpointIDsIterator->value.append(id); + + m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.set(id, breakpointIdentifier); +} + +void InspectorDebuggerAgent::resolveBreakpoint(const Script& script, JSC::Breakpoint& breakpoint) +{ + if (breakpoint.line < static_cast<unsigned>(script.startLine) || static_cast<unsigned>(script.endLine) < breakpoint.line) + return; + + m_scriptDebugServer.resolveBreakpoint(breakpoint, script.sourceProvider.get()); +} + +void InspectorDebuggerAgent::setBreakpoint(JSC::Breakpoint& breakpoint, bool& existing) +{ + JSC::JSLockHolder locker(m_scriptDebugServer.vm()); + m_scriptDebugServer.setBreakpoint(breakpoint, existing); +} + +void InspectorDebuggerAgent::removeBreakpoint(ErrorString&, const String& breakpointIdentifier) { m_javaScriptBreakpoints.remove(breakpointIdentifier); - Vector<JSC::BreakpointID> breakpointIDs = m_breakpointIdentifierToDebugServerBreakpointIDs.take(breakpointIdentifier); - for (auto breakpointID : breakpointIDs) { - const Vector<ScriptBreakpointAction>& breakpointActions = scriptDebugServer().getActionsForBreakpoint(breakpointID); + for (JSC::BreakpointID breakpointID : m_breakpointIdentifierToDebugServerBreakpointIDs.take(breakpointIdentifier)) { + m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.remove(breakpointID); + + const BreakpointActions& breakpointActions = m_scriptDebugServer.getActionsForBreakpoint(breakpointID); for (auto& action : breakpointActions) - m_injectedScriptManager->releaseObjectGroup(objectGroupForBreakpointAction(action.identifier)); + m_injectedScriptManager.releaseObjectGroup(objectGroupForBreakpointAction(action)); - scriptDebugServer().removeBreakpoint(breakpointID); + JSC::JSLockHolder locker(m_scriptDebugServer.vm()); + m_scriptDebugServer.removeBreakpointActions(breakpointID); + m_scriptDebugServer.removeBreakpoint(breakpointID); } } -void InspectorDebuggerAgent::continueToLocation(ErrorString* errorString, const RefPtr<InspectorObject>& location) +void InspectorDebuggerAgent::continueUntilNextRunLoop(ErrorString& errorString) +{ + if (!assertPaused(errorString)) + return; + + resume(errorString); + + m_enablePauseWhenIdle = true; + + registerIdleHandler(); +} + +void InspectorDebuggerAgent::continueToLocation(ErrorString& errorString, const InspectorObject& location) { + if (!assertPaused(errorString)) + return; + if (m_continueToLocationBreakpointID != JSC::noBreakpointID) { - scriptDebugServer().removeBreakpoint(m_continueToLocationBreakpointID); + m_scriptDebugServer.removeBreakpoint(m_continueToLocationBreakpointID); m_continueToLocationBreakpointID = JSC::noBreakpointID; } JSC::SourceID sourceID; unsigned lineNumber; unsigned columnNumber; - if (!parseLocation(errorString, location.get(), &sourceID, &lineNumber, &columnNumber)) + if (!parseLocation(errorString, location, sourceID, lineNumber, columnNumber)) return; - ScriptBreakpoint breakpoint(lineNumber, columnNumber, "", false); - m_continueToLocationBreakpointID = scriptDebugServer().setBreakpoint(sourceID, breakpoint, &lineNumber, &columnNumber); - resume(errorString); -} + auto scriptIterator = m_scripts.find(sourceID); + if (scriptIterator == m_scripts.end()) { + m_scriptDebugServer.continueProgram(); + m_frontendDispatcher->resumed(); + errorString = ASCIILiteral("No script for id: ") + String::number(sourceID); + return; + } -PassRefPtr<Inspector::TypeBuilder::Debugger::Location> InspectorDebuggerAgent::resolveBreakpoint(const String& breakpointIdentifier, JSC::SourceID sourceID, const ScriptBreakpoint& breakpoint) -{ - ScriptsMap::iterator scriptIterator = m_scripts.find(sourceID); - if (scriptIterator == m_scripts.end()) - return nullptr; + String condition; + bool autoContinue = false; + unsigned ignoreCount = 0; + JSC::Breakpoint breakpoint(sourceID, lineNumber, columnNumber, condition, autoContinue, ignoreCount); Script& script = scriptIterator->value; - if (breakpoint.lineNumber < script.startLine || script.endLine < breakpoint.lineNumber) - return nullptr; + resolveBreakpoint(script, breakpoint); + if (!breakpoint.resolved) { + m_scriptDebugServer.continueProgram(); + m_frontendDispatcher->resumed(); + errorString = ASCIILiteral("Could not resolve breakpoint"); + return; + } - unsigned actualLineNumber; - unsigned actualColumnNumber; - JSC::BreakpointID debugServerBreakpointID = scriptDebugServer().setBreakpoint(sourceID, breakpoint, &actualLineNumber, &actualColumnNumber); - if (debugServerBreakpointID == JSC::noBreakpointID) - return nullptr; + bool existing; + setBreakpoint(breakpoint, existing); + if (existing) { + // There is an existing breakpoint at this location. Instead of + // acting like a series of steps, just resume and we will either + // hit this new breakpoint or not. + m_scriptDebugServer.continueProgram(); + m_frontendDispatcher->resumed(); + return; + } - BreakpointIdentifierToDebugServerBreakpointIDsMap::iterator debugServerBreakpointIDsIterator = m_breakpointIdentifierToDebugServerBreakpointIDs.find(breakpointIdentifier); - if (debugServerBreakpointIDsIterator == m_breakpointIdentifierToDebugServerBreakpointIDs.end()) - debugServerBreakpointIDsIterator = m_breakpointIdentifierToDebugServerBreakpointIDs.set(breakpointIdentifier, Vector<JSC::BreakpointID>()).iterator; - debugServerBreakpointIDsIterator->value.append(debugServerBreakpointID); + m_continueToLocationBreakpointID = breakpoint.id; - RefPtr<Inspector::TypeBuilder::Debugger::Location> location = Inspector::TypeBuilder::Debugger::Location::create() - .setScriptId(String::number(sourceID)) - .setLineNumber(actualLineNumber); - location->setColumnNumber(actualColumnNumber); - return location; + // Treat this as a series of steps until reaching the new breakpoint. + // So don't issue a resumed event unless we exit the VM without pausing. + willStepAndMayBecomeIdle(); + m_scriptDebugServer.continueProgram(); } -void InspectorDebuggerAgent::searchInContent(ErrorString* error, const String& scriptIDStr, const String& query, const bool* const optionalCaseSensitive, const bool* const optionalIsRegex, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::GenericTypes::SearchMatch>>& results) +void InspectorDebuggerAgent::searchInContent(ErrorString& error, const String& scriptIDStr, const String& query, const bool* optionalCaseSensitive, const bool* optionalIsRegex, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::GenericTypes::SearchMatch>>& results) { + JSC::SourceID sourceID = scriptIDStr.toIntPtr(); + auto it = m_scripts.find(sourceID); + if (it == m_scripts.end()) { + error = ASCIILiteral("No script for id: ") + scriptIDStr; + return; + } + bool isRegex = optionalIsRegex ? *optionalIsRegex : false; bool caseSensitive = optionalCaseSensitive ? *optionalCaseSensitive : false; - - JSC::SourceID sourceID = scriptIDStr.toIntPtr(); - ScriptsMap::iterator it = m_scripts.find(sourceID); - if (it != m_scripts.end()) - results = ContentSearchUtilities::searchInTextByLines(it->value.source, query, caseSensitive, isRegex); - else - *error = "No script for id: " + scriptIDStr; + results = ContentSearchUtilities::searchInTextByLines(it->value.source, query, caseSensitive, isRegex); } -void InspectorDebuggerAgent::getScriptSource(ErrorString* error, const String& scriptIDStr, String* scriptSource) +void InspectorDebuggerAgent::getScriptSource(ErrorString& error, const String& scriptIDStr, String* scriptSource) { JSC::SourceID sourceID = scriptIDStr.toIntPtr(); ScriptsMap::iterator it = m_scripts.find(sourceID); if (it != m_scripts.end()) *scriptSource = it->value.source; else - *error = "No script for id: " + scriptIDStr; + error = ASCIILiteral("No script for id: ") + scriptIDStr; } -void InspectorDebuggerAgent::getFunctionDetails(ErrorString* errorString, const String& functionId, RefPtr<Inspector::TypeBuilder::Debugger::FunctionDetails>& details) +void InspectorDebuggerAgent::getFunctionDetails(ErrorString& errorString, const String& functionId, RefPtr<Inspector::Protocol::Debugger::FunctionDetails>& details) { - InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(functionId); + InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(functionId); if (injectedScript.hasNoValue()) { - *errorString = ASCIILiteral("Function object id is obsolete"); + errorString = ASCIILiteral("Function object id is obsolete"); return; } injectedScript.getFunctionDetails(errorString, functionId, &details); } -void InspectorDebuggerAgent::schedulePauseOnNextStatement(InspectorDebuggerFrontendDispatcher::Reason::Enum breakReason, PassRefPtr<InspectorObject> data) +void InspectorDebuggerAgent::schedulePauseOnNextStatement(DebuggerFrontendDispatcher::Reason breakReason, RefPtr<InspectorObject>&& data) { if (m_javaScriptPauseScheduled) return; + m_javaScriptPauseScheduled = true; + m_breakReason = breakReason; - m_breakAuxData = data; - scriptDebugServer().setPauseOnNextStatement(true); + m_breakAuxData = WTFMove(data); + + JSC::JSLockHolder locker(m_scriptDebugServer.vm()); + m_scriptDebugServer.setPauseOnNextStatement(true); } void InspectorDebuggerAgent::cancelPauseOnNextStatement() { - if (m_javaScriptPauseScheduled) + if (!m_javaScriptPauseScheduled) return; + m_javaScriptPauseScheduled = false; + clearBreakDetails(); - scriptDebugServer().setPauseOnNextStatement(false); + m_scriptDebugServer.setPauseOnNextStatement(false); + m_enablePauseWhenIdle = false; } -void InspectorDebuggerAgent::pause(ErrorString*) +void InspectorDebuggerAgent::pause(ErrorString&) { - if (m_javaScriptPauseScheduled) + schedulePauseOnNextStatement(DebuggerFrontendDispatcher::Reason::PauseOnNextStatement, nullptr); +} + +void InspectorDebuggerAgent::resume(ErrorString& errorString) +{ + if (!m_pausedScriptState && !m_javaScriptPauseScheduled) { + errorString = ASCIILiteral("Was not paused or waiting to pause"); return; + } - clearBreakDetails(); - scriptDebugServer().setPauseOnNextStatement(true); - m_javaScriptPauseScheduled = true; + cancelPauseOnNextStatement(); + m_scriptDebugServer.continueProgram(); + m_conditionToDispatchResumed = ShouldDispatchResumed::WhenContinued; } -void InspectorDebuggerAgent::resume(ErrorString* errorString) +void InspectorDebuggerAgent::stepOver(ErrorString& errorString) { if (!assertPaused(errorString)) return; - m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup); - scriptDebugServer().continueProgram(); + willStepAndMayBecomeIdle(); + m_scriptDebugServer.stepOverStatement(); } -void InspectorDebuggerAgent::stepOver(ErrorString* errorString) +void InspectorDebuggerAgent::stepInto(ErrorString& errorString) { if (!assertPaused(errorString)) return; - m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup); - scriptDebugServer().stepOverStatement(); + willStepAndMayBecomeIdle(); + m_scriptDebugServer.stepIntoStatement(); } -void InspectorDebuggerAgent::stepInto(ErrorString* errorString) +void InspectorDebuggerAgent::stepOut(ErrorString& errorString) { if (!assertPaused(errorString)) return; - m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup); - scriptDebugServer().stepIntoStatement(); - m_listener->stepInto(); + willStepAndMayBecomeIdle(); + m_scriptDebugServer.stepOutOfFunction(); } -void InspectorDebuggerAgent::stepOut(ErrorString* errorString) +void InspectorDebuggerAgent::registerIdleHandler() { - if (!assertPaused(errorString)) - return; + if (!m_registeredIdleCallback) { + m_registeredIdleCallback = true; + JSC::VM& vm = m_scriptDebugServer.vm(); + vm.whenIdle([this]() { + didBecomeIdle(); + }); + } +} - m_injectedScriptManager->releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup); - scriptDebugServer().stepOutOfFunction(); +void InspectorDebuggerAgent::willStepAndMayBecomeIdle() +{ + // When stepping the backend must eventually trigger a "paused" or "resumed" event. + // If the step causes us to exit the VM, then we should issue "resumed". + m_conditionToDispatchResumed = ShouldDispatchResumed::WhenIdle; + + registerIdleHandler(); } -void InspectorDebuggerAgent::setPauseOnExceptions(ErrorString* errorString, const String& stringPauseState) +void InspectorDebuggerAgent::didBecomeIdle() +{ + m_registeredIdleCallback = false; + + if (m_conditionToDispatchResumed == ShouldDispatchResumed::WhenIdle) { + cancelPauseOnNextStatement(); + m_scriptDebugServer.continueProgram(); + m_frontendDispatcher->resumed(); + } + + m_conditionToDispatchResumed = ShouldDispatchResumed::No; + + if (m_enablePauseWhenIdle) { + ErrorString ignored; + pause(ignored); + } +} + +void InspectorDebuggerAgent::setPauseOnExceptions(ErrorString& errorString, const String& stringPauseState) { JSC::Debugger::PauseOnExceptionsState pauseState; if (stringPauseState == "none") @@ -506,114 +807,139 @@ void InspectorDebuggerAgent::setPauseOnExceptions(ErrorString* errorString, cons else if (stringPauseState == "uncaught") pauseState = JSC::Debugger::PauseOnUncaughtExceptions; else { - *errorString = "Unknown pause on exceptions mode: " + stringPauseState; + errorString = ASCIILiteral("Unknown pause on exceptions mode: ") + stringPauseState; return; } - scriptDebugServer().setPauseOnExceptionsState(static_cast<JSC::Debugger::PauseOnExceptionsState>(pauseState)); - if (scriptDebugServer().pauseOnExceptionsState() != pauseState) - *errorString = ASCIILiteral("Internal error. Could not change pause on exceptions state"); + m_scriptDebugServer.setPauseOnExceptionsState(static_cast<JSC::Debugger::PauseOnExceptionsState>(pauseState)); + if (m_scriptDebugServer.pauseOnExceptionsState() != pauseState) + errorString = ASCIILiteral("Internal error. Could not change pause on exceptions state"); +} + +void InspectorDebuggerAgent::setPauseOnAssertions(ErrorString&, bool enabled) +{ + m_pauseOnAssertionFailures = enabled; } -void InspectorDebuggerAgent::evaluateOnCallFrame(ErrorString* errorString, const String& callFrameId, const String& expression, const String* const objectGroup, const bool* const includeCommandLineAPI, const bool* const doNotPauseOnExceptionsAndMuteConsole, const bool* const returnByValue, const bool* generatePreview, RefPtr<Inspector::TypeBuilder::Runtime::RemoteObject>& result, Inspector::TypeBuilder::OptOutput<bool>* wasThrown) +void InspectorDebuggerAgent::evaluateOnCallFrame(ErrorString& errorString, const String& callFrameId, const String& expression, const String* const objectGroup, const bool* const includeCommandLineAPI, const bool* const doNotPauseOnExceptionsAndMuteConsole, 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) { - InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(callFrameId); + if (m_currentCallStack.hasNoValue()) { + errorString = ASCIILiteral("Not paused"); + return; + } + + InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(callFrameId); if (injectedScript.hasNoValue()) { - *errorString = ASCIILiteral("Inspected frame has gone"); + errorString = ASCIILiteral("Could not find InjectedScript for callFrameId"); return; } - JSC::Debugger::PauseOnExceptionsState previousPauseOnExceptionsState = scriptDebugServer().pauseOnExceptionsState(); + JSC::Debugger::PauseOnExceptionsState previousPauseOnExceptionsState = m_scriptDebugServer.pauseOnExceptionsState(); if (doNotPauseOnExceptionsAndMuteConsole ? *doNotPauseOnExceptionsAndMuteConsole : false) { if (previousPauseOnExceptionsState != JSC::Debugger::DontPauseOnExceptions) - scriptDebugServer().setPauseOnExceptionsState(JSC::Debugger::DontPauseOnExceptions); + m_scriptDebugServer.setPauseOnExceptionsState(JSC::Debugger::DontPauseOnExceptions); muteConsole(); } - injectedScript.evaluateOnCallFrame(errorString, m_currentCallStack, callFrameId, expression, objectGroup ? *objectGroup : "", includeCommandLineAPI ? *includeCommandLineAPI : false, returnByValue ? *returnByValue : false, generatePreview ? *generatePreview : false, &result, wasThrown); + injectedScript.evaluateOnCallFrame(errorString, m_currentCallStack, callFrameId, expression, objectGroup ? *objectGroup : "", includeCommandLineAPI ? *includeCommandLineAPI : false, returnByValue ? *returnByValue : false, generatePreview ? *generatePreview : false, saveResult ? *saveResult : false, &result, wasThrown, savedResultIndex); if (doNotPauseOnExceptionsAndMuteConsole ? *doNotPauseOnExceptionsAndMuteConsole : false) { unmuteConsole(); - if (scriptDebugServer().pauseOnExceptionsState() != previousPauseOnExceptionsState) - scriptDebugServer().setPauseOnExceptionsState(previousPauseOnExceptionsState); + if (m_scriptDebugServer.pauseOnExceptionsState() != previousPauseOnExceptionsState) + m_scriptDebugServer.setPauseOnExceptionsState(previousPauseOnExceptionsState); } } -void InspectorDebuggerAgent::setOverlayMessage(ErrorString*, const String*) +void InspectorDebuggerAgent::setOverlayMessage(ErrorString&, const String*) { } void InspectorDebuggerAgent::scriptExecutionBlockedByCSP(const String& directiveText) { - if (scriptDebugServer().pauseOnExceptionsState() != JSC::Debugger::DontPauseOnExceptions) { - RefPtr<InspectorObject> directive = InspectorObject::create(); - directive->setString(ASCIILiteral("directiveText"), directiveText); - breakProgram(InspectorDebuggerFrontendDispatcher::Reason::CSPViolation, directive.release()); - } + if (m_scriptDebugServer.pauseOnExceptionsState() != JSC::Debugger::DontPauseOnExceptions) + breakProgram(DebuggerFrontendDispatcher::Reason::CSPViolation, buildCSPViolationPauseReason(directiveText)); } -PassRefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Debugger::CallFrame>> InspectorDebuggerAgent::currentCallFrames() +Ref<Inspector::Protocol::Array<Inspector::Protocol::Debugger::CallFrame>> InspectorDebuggerAgent::currentCallFrames(const InjectedScript& injectedScript) { - if (!m_pausedScriptState) - return Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Debugger::CallFrame>::create(); - - InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(m_pausedScriptState); - if (injectedScript.hasNoValue()) { - ASSERT_NOT_REACHED(); - return Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Debugger::CallFrame>::create(); - } + ASSERT(!injectedScript.hasNoValue()); + if (injectedScript.hasNoValue()) + return Inspector::Protocol::Array<Inspector::Protocol::Debugger::CallFrame>::create(); return injectedScript.wrapCallFrames(m_currentCallStack); } String InspectorDebuggerAgent::sourceMapURLForScript(const Script& script) { - return ContentSearchUtilities::findScriptSourceMapURL(script.source); + return script.sourceMappingURL; } -void InspectorDebuggerAgent::didParseSource(JSC::SourceID sourceID, const Script& inScript) +static bool isWebKitInjectedScript(const String& sourceURL) { - Script script = inScript; - if (script.startLine <= 0 && !script.startColumn) - script.sourceURL = ContentSearchUtilities::findScriptSourceURL(script.source); - script.sourceMappingURL = sourceMapURLForScript(script); + return sourceURL.startsWith("__InjectedScript_") && sourceURL.endsWith(".js"); +} +void InspectorDebuggerAgent::didParseSource(JSC::SourceID sourceID, const Script& script) +{ + String scriptIDStr = String::number(sourceID); bool hasSourceURL = !script.sourceURL.isEmpty(); - String scriptURL = hasSourceURL ? script.sourceURL : script.url; - bool* hasSourceURLParam = hasSourceURL ? &hasSourceURL : nullptr; - String* sourceMapURLParam = script.sourceMappingURL.isNull() ? nullptr : &script.sourceMappingURL; + String sourceURL = script.sourceURL; + String sourceMappingURL = sourceMapURLForScript(script); + + const bool isModule = script.sourceProvider->sourceType() == JSC::SourceProviderSourceType::Module; const bool* isContentScript = script.isContentScript ? &script.isContentScript : nullptr; - String scriptIDStr = String::number(sourceID); - m_frontendDispatcher->scriptParsed(scriptIDStr, scriptURL, script.startLine, script.startColumn, script.endLine, script.endColumn, isContentScript, sourceMapURLParam, hasSourceURLParam); + String* sourceURLParam = hasSourceURL ? &sourceURL : nullptr; + String* sourceMapURLParam = sourceMappingURL.isEmpty() ? nullptr : &sourceMappingURL; + + m_frontendDispatcher->scriptParsed(scriptIDStr, script.url, script.startLine, script.startColumn, script.endLine, script.endColumn, isContentScript, sourceURLParam, sourceMapURLParam, isModule ? &isModule : nullptr); m_scripts.set(sourceID, script); - if (scriptURL.isEmpty()) + if (hasSourceURL && isWebKitInjectedScript(sourceURL)) + m_scriptDebugServer.addToBlacklist(sourceID); + + String scriptURLForBreakpoints = hasSourceURL ? script.sourceURL : script.url; + if (scriptURLForBreakpoints.isEmpty()) return; - for (auto it = m_javaScriptBreakpoints.begin(), end = m_javaScriptBreakpoints.end(); it != end; ++it) { - RefPtr<InspectorObject> breakpointObject = it->value->asObject(); + for (auto& entry : m_javaScriptBreakpoints) { + RefPtr<InspectorObject> breakpointObject = entry.value; + bool isRegex; - breakpointObject->getBoolean(ASCIILiteral("isRegex"), &isRegex); String url; - breakpointObject->getString(ASCIILiteral("url"), &url); - if (!matches(scriptURL, url, isRegex)) + breakpointObject->getBoolean(ASCIILiteral("isRegex"), isRegex); + breakpointObject->getString(ASCIILiteral("url"), url); + if (!matches(scriptURLForBreakpoints, url, isRegex)) continue; - ScriptBreakpoint breakpoint; - breakpointObject->getNumber(ASCIILiteral("lineNumber"), &breakpoint.lineNumber); - breakpointObject->getNumber(ASCIILiteral("columnNumber"), &breakpoint.columnNumber); - breakpointObject->getString(ASCIILiteral("condition"), &breakpoint.condition); - breakpointObject->getBoolean(ASCIILiteral("autoContinue"), &breakpoint.autoContinue); + + ScriptBreakpoint scriptBreakpoint; + breakpointObject->getInteger(ASCIILiteral("lineNumber"), scriptBreakpoint.lineNumber); + breakpointObject->getInteger(ASCIILiteral("columnNumber"), scriptBreakpoint.columnNumber); + breakpointObject->getString(ASCIILiteral("condition"), scriptBreakpoint.condition); + breakpointObject->getBoolean(ASCIILiteral("autoContinue"), scriptBreakpoint.autoContinue); + breakpointObject->getInteger(ASCIILiteral("ignoreCount"), scriptBreakpoint.ignoreCount); ErrorString errorString; - RefPtr<InspectorArray> actions = breakpointObject->getArray(ASCIILiteral("actions")); - if (!breakpointActionsFromProtocol(&errorString, actions, &breakpoint.actions)) { + RefPtr<InspectorArray> actions; + breakpointObject->getArray(ASCIILiteral("actions"), actions); + if (!breakpointActionsFromProtocol(errorString, actions, &scriptBreakpoint.actions)) { ASSERT_NOT_REACHED(); continue; } - RefPtr<Inspector::TypeBuilder::Debugger::Location> location = resolveBreakpoint(it->key, sourceID, breakpoint); - if (location) - m_frontendDispatcher->breakpointResolved(it->key, location); + JSC::Breakpoint breakpoint(sourceID, scriptBreakpoint.lineNumber, scriptBreakpoint.columnNumber, scriptBreakpoint.condition, scriptBreakpoint.autoContinue, scriptBreakpoint.ignoreCount); + resolveBreakpoint(script, breakpoint); + if (!breakpoint.resolved) + continue; + + bool existing; + setBreakpoint(breakpoint, existing); + if (existing) + continue; + + String breakpointIdentifier = entry.key; + didSetBreakpoint(breakpoint, breakpointIdentifier, scriptBreakpoint); + + m_frontendDispatcher->breakpointResolved(breakpointIdentifier, buildDebuggerLocation(breakpoint)); } } @@ -622,94 +948,173 @@ void InspectorDebuggerAgent::failedToParseSource(const String& url, const String m_frontendDispatcher->scriptFailedToParse(url, data, firstLine, errorLine, errorMessage); } -void InspectorDebuggerAgent::didPause(JSC::ExecState* scriptState, const Deprecated::ScriptValue& callFrames, const Deprecated::ScriptValue& exception) +void InspectorDebuggerAgent::didPause(JSC::ExecState& scriptState, JSC::JSValue callFrames, JSC::JSValue exceptionOrCaughtValue) { - ASSERT(scriptState && !m_pausedScriptState); - m_pausedScriptState = scriptState; - m_currentCallStack = callFrames; - - if (!exception.hasNoValue()) { - InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState); - if (!injectedScript.hasNoValue()) { - m_breakReason = InspectorDebuggerFrontendDispatcher::Reason::Exception; - m_breakAuxData = injectedScript.wrapObject(exception, InspectorDebuggerAgent::backtraceObjectGroup)->openAccessors(); - // m_breakAuxData might be null after this. + ASSERT(!m_pausedScriptState); + m_pausedScriptState = &scriptState; + m_currentCallStack = { scriptState.vm(), callFrames }; + + InjectedScript injectedScript = m_injectedScriptManager.injectedScriptFor(&scriptState); + + // If a high level pause pause reason is not already set, try to infer a reason from the debugger. + if (m_breakReason == DebuggerFrontendDispatcher::Reason::Other) { + switch (m_scriptDebugServer.reasonForPause()) { + case JSC::Debugger::PausedForBreakpoint: { + JSC::BreakpointID debuggerBreakpointId = m_scriptDebugServer.pausingBreakpointID(); + if (debuggerBreakpointId != m_continueToLocationBreakpointID) { + m_breakReason = DebuggerFrontendDispatcher::Reason::Breakpoint; + m_breakAuxData = buildBreakpointPauseReason(debuggerBreakpointId); + } + break; } + case JSC::Debugger::PausedForDebuggerStatement: + m_breakReason = DebuggerFrontendDispatcher::Reason::DebuggerStatement; + m_breakAuxData = nullptr; + break; + case JSC::Debugger::PausedForException: + m_breakReason = DebuggerFrontendDispatcher::Reason::Exception; + m_breakAuxData = buildExceptionPauseReason(exceptionOrCaughtValue, injectedScript); + break; + case JSC::Debugger::PausedAtStatement: + case JSC::Debugger::PausedAtExpression: + case JSC::Debugger::PausedBeforeReturn: + case JSC::Debugger::PausedAtEndOfProgram: + // Pause was just stepping. Nothing to report. + break; + case JSC::Debugger::NotPaused: + ASSERT_NOT_REACHED(); + break; + } + } + + // Set $exception to the exception or caught value. + if (exceptionOrCaughtValue && !injectedScript.hasNoValue()) { + injectedScript.setExceptionValue(exceptionOrCaughtValue); + m_hasExceptionValue = true; } - m_frontendDispatcher->paused(currentCallFrames(), m_breakReason, m_breakAuxData); + m_conditionToDispatchResumed = ShouldDispatchResumed::No; + m_enablePauseWhenIdle = false; + + RefPtr<Inspector::Protocol::Console::StackTrace> asyncStackTrace; + if (m_currentAsyncCallIdentifier) { + auto it = m_pendingAsyncCalls.find(m_currentAsyncCallIdentifier.value()); + if (it != m_pendingAsyncCalls.end()) + asyncStackTrace = it->value->buildInspectorObject(); + } + + m_frontendDispatcher->paused(currentCallFrames(injectedScript), m_breakReason, m_breakAuxData, asyncStackTrace); + m_javaScriptPauseScheduled = false; if (m_continueToLocationBreakpointID != JSC::noBreakpointID) { - scriptDebugServer().removeBreakpoint(m_continueToLocationBreakpointID); + m_scriptDebugServer.removeBreakpoint(m_continueToLocationBreakpointID); m_continueToLocationBreakpointID = JSC::noBreakpointID; } - if (m_listener) - m_listener->didPause(); + RefPtr<Stopwatch> stopwatch = m_injectedScriptManager.inspectorEnvironment().executionStopwatch(); + if (stopwatch && stopwatch->isActive()) { + stopwatch->stop(); + m_didPauseStopwatch = true; + } } -void InspectorDebuggerAgent::didSampleProbe(JSC::ExecState* scriptState, int probeIdentifier, int hitCount, const Deprecated::ScriptValue& sample) +void InspectorDebuggerAgent::breakpointActionSound(int breakpointActionIdentifier) { - int sampleId = m_nextProbeSampleId++; - - InjectedScript injectedScript = m_injectedScriptManager->injectedScriptFor(scriptState); - RefPtr<TypeBuilder::Runtime::RemoteObject> payload = injectedScript.wrapObject(sample, objectGroupForBreakpointAction(probeIdentifier)); - RefPtr<TypeBuilder::Debugger::ProbeSample> result = TypeBuilder::Debugger::ProbeSample::create() - .setProbeId(probeIdentifier) - .setSampleId(sampleId) - .setBatchId(hitCount) - .setTimestamp(monotonicallyIncreasingTime()) - .setPayload(payload.release()); - - m_frontendDispatcher->didSampleProbe(result.release()); + m_frontendDispatcher->playBreakpointActionSound(breakpointActionIdentifier); } -void InspectorDebuggerAgent::breakpointActionSound() +void InspectorDebuggerAgent::breakpointActionProbe(JSC::ExecState& scriptState, const ScriptBreakpointAction& action, unsigned batchId, unsigned sampleId, JSC::JSValue sample) { - // FIXME: We should send a message to the frontend to make the frontend beep. + InjectedScript injectedScript = m_injectedScriptManager.injectedScriptFor(&scriptState); + auto payload = injectedScript.wrapObject(sample, objectGroupForBreakpointAction(action), true); + auto result = Protocol::Debugger::ProbeSample::create() + .setProbeId(action.identifier) + .setBatchId(batchId) + .setSampleId(sampleId) + .setTimestamp(m_injectedScriptManager.inspectorEnvironment().executionStopwatch()->elapsedTime()) + .setPayload(WTFMove(payload)) + .release(); + m_frontendDispatcher->didSampleProbe(WTFMove(result)); } void InspectorDebuggerAgent::didContinue() { + if (m_didPauseStopwatch) { + m_didPauseStopwatch = false; + m_injectedScriptManager.inspectorEnvironment().executionStopwatch()->start(); + } + m_pausedScriptState = nullptr; - m_currentCallStack = Deprecated::ScriptValue(); + m_currentCallStack = { }; + m_injectedScriptManager.releaseObjectGroup(InspectorDebuggerAgent::backtraceObjectGroup); clearBreakDetails(); + clearExceptionValue(); - m_frontendDispatcher->resumed(); + if (m_conditionToDispatchResumed == ShouldDispatchResumed::WhenContinued) + m_frontendDispatcher->resumed(); } -void InspectorDebuggerAgent::breakProgram(InspectorDebuggerFrontendDispatcher::Reason::Enum breakReason, PassRefPtr<InspectorObject> data) +void InspectorDebuggerAgent::breakProgram(DebuggerFrontendDispatcher::Reason breakReason, RefPtr<InspectorObject>&& data) { m_breakReason = breakReason; - m_breakAuxData = data; - scriptDebugServer().breakProgram(); + m_breakAuxData = WTFMove(data); + m_scriptDebugServer.breakProgram(); } -void InspectorDebuggerAgent::clearResolvedBreakpointState() +void InspectorDebuggerAgent::clearInspectorBreakpointState() { ErrorString dummyError; Vector<String> breakpointIdentifiers; copyKeysToVector(m_breakpointIdentifierToDebugServerBreakpointIDs, breakpointIdentifiers); for (const String& identifier : breakpointIdentifiers) - removeBreakpoint(&dummyError, identifier); + removeBreakpoint(dummyError, identifier); - scriptDebugServer().continueProgram(); + m_javaScriptBreakpoints.clear(); + + clearDebuggerBreakpointState(); +} + +void InspectorDebuggerAgent::clearDebuggerBreakpointState() +{ + { + JSC::JSLockHolder holder(m_scriptDebugServer.vm()); + m_scriptDebugServer.clearBreakpointActions(); + m_scriptDebugServer.clearBreakpoints(); + m_scriptDebugServer.clearBlacklist(); + } m_pausedScriptState = nullptr; - m_currentCallStack = Deprecated::ScriptValue(); + m_currentCallStack = { }; m_scripts.clear(); m_breakpointIdentifierToDebugServerBreakpointIDs.clear(); + m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier.clear(); m_continueToLocationBreakpointID = JSC::noBreakpointID; clearBreakDetails(); m_javaScriptPauseScheduled = false; - setOverlayMessage(&dummyError, nullptr); + m_hasExceptionValue = false; + + if (isPaused()) { + m_scriptDebugServer.continueProgram(); + m_frontendDispatcher->resumed(); + } } -bool InspectorDebuggerAgent::assertPaused(ErrorString* errorString) +void InspectorDebuggerAgent::didClearGlobalObject() +{ + // Clear breakpoints from the debugger, but keep the inspector's model of which + // pages have what breakpoints, as the mapping is only sent to DebuggerAgent once. + clearDebuggerBreakpointState(); + + clearAsyncStackTraceData(); + + m_frontendDispatcher->globalObjectCleared(); +} + +bool InspectorDebuggerAgent::assertPaused(ErrorString& errorString) { if (!m_pausedScriptState) { - *errorString = ASCIILiteral("Can only perform operation while paused."); + errorString = ASCIILiteral("Can only perform operation while paused."); return false; } @@ -718,18 +1123,22 @@ bool InspectorDebuggerAgent::assertPaused(ErrorString* errorString) void InspectorDebuggerAgent::clearBreakDetails() { - m_breakReason = InspectorDebuggerFrontendDispatcher::Reason::Other; + m_breakReason = DebuggerFrontendDispatcher::Reason::Other; m_breakAuxData = nullptr; } -void InspectorDebuggerAgent::didClearGlobalObject() +void InspectorDebuggerAgent::clearExceptionValue() { - if (m_frontendDispatcher) - m_frontendDispatcher->globalObjectCleared(); + if (m_hasExceptionValue) { + m_injectedScriptManager.clearExceptionValue(); + m_hasExceptionValue = false; + } +} - clearResolvedBreakpointState(); +void InspectorDebuggerAgent::clearAsyncStackTraceData() +{ + m_pendingAsyncCalls.clear(); + m_currentAsyncCallIdentifier = std::nullopt; } } // namespace Inspector - -#endif // ENABLE(INSPECTOR) diff --git a/Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.h b/Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.h index 0e652499d..6f8132ac2 100644 --- a/Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.h +++ b/Source/JavaScriptCore/inspector/agents/InspectorDebuggerAgent.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2010, 2013 Apple Inc. All rights reserved. - * Copyright (C) 2010-2011 Google Inc. All rights reserved. + * Copyright (C) 2010, 2013, 2015-2016 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,7 +11,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -27,37 +27,32 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef InspectorDebuggerAgent_h -#define InspectorDebuggerAgent_h +#pragma once -#if ENABLE(INSPECTOR) - -#include "InspectorJSBackendDispatchers.h" -#include "InspectorJSFrontendDispatchers.h" +#include "InspectorBackendDispatchers.h" +#include "InspectorFrontendDispatchers.h" #include "bindings/ScriptValue.h" #include "debugger/Debugger.h" #include "inspector/InspectorAgentBase.h" #include "inspector/ScriptBreakpoint.h" +#include "inspector/ScriptCallStack.h" #include "inspector/ScriptDebugListener.h" #include <wtf/Forward.h> #include <wtf/HashMap.h> #include <wtf/Noncopyable.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/PassRefPtr.h> #include <wtf/Vector.h> -#include <wtf/text/StringHash.h> namespace Inspector { +class AsyncStackTrace; class InjectedScript; class InjectedScriptManager; class InspectorArray; class InspectorObject; -class InspectorValue; class ScriptDebugServer; typedef String ErrorString; -class JS_EXPORT_PRIVATE InspectorDebuggerAgent : public InspectorAgentBase, public ScriptDebugListener, public InspectorDebuggerBackendDispatcherHandler { +class JS_EXPORT_PRIVATE InspectorDebuggerAgent : public InspectorAgentBase, public ScriptDebugListener, public DebuggerBackendDispatcherHandler { WTF_MAKE_NONCOPYABLE(InspectorDebuggerAgent); WTF_MAKE_FAST_ALLOCATED; public: @@ -65,35 +60,48 @@ public: virtual ~InspectorDebuggerAgent(); - virtual void didCreateFrontendAndBackend(InspectorFrontendChannel*, InspectorBackendDispatcher*) override; - virtual void willDestroyFrontendAndBackend(InspectorDisconnectReason) override; - - virtual void enable(ErrorString*) override; - virtual void disable(ErrorString*) override; - virtual void setBreakpointsActive(ErrorString*, bool active) override; - virtual void setBreakpointByUrl(ErrorString*, int lineNumber, const String* optionalURL, const String* optionalURLRegex, const int* optionalColumnNumber, const RefPtr<Inspector::InspectorObject>* options, Inspector::TypeBuilder::Debugger::BreakpointId*, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Debugger::Location>>& locations, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Debugger::BreakpointActionIdentifier>>& breakpointActionIdentifiers) override; - virtual void setBreakpoint(ErrorString*, const RefPtr<Inspector::InspectorObject>& location, const RefPtr<Inspector::InspectorObject>* options, Inspector::TypeBuilder::Debugger::BreakpointId*, RefPtr<Inspector::TypeBuilder::Debugger::Location>& actualLocation, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Debugger::BreakpointActionIdentifier>>& breakpointActionIdentifiers) override; - virtual void removeBreakpoint(ErrorString*, const String& breakpointIdentifier) override; - virtual void continueToLocation(ErrorString*, const RefPtr<InspectorObject>& location) override; - virtual void searchInContent(ErrorString*, const String& scriptID, const String& query, const bool* optionalCaseSensitive, const bool* optionalIsRegex, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::GenericTypes::SearchMatch>>&) override; - virtual void getScriptSource(ErrorString*, const String& scriptID, String* scriptSource) override; - virtual void getFunctionDetails(ErrorString*, const String& functionId, RefPtr<Inspector::TypeBuilder::Debugger::FunctionDetails>&) override; - virtual void pause(ErrorString*) override; - virtual void resume(ErrorString*) override; - virtual void stepOver(ErrorString*) override; - virtual void stepInto(ErrorString*) override; - virtual void stepOut(ErrorString*) override; - virtual void setPauseOnExceptions(ErrorString*, const String& pauseState) override; - virtual void evaluateOnCallFrame(ErrorString*, const String& callFrameId, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, RefPtr<Inspector::TypeBuilder::Runtime::RemoteObject>& result, Inspector::TypeBuilder::OptOutput<bool>* wasThrown) override; - virtual void setOverlayMessage(ErrorString*, const String*) override; - - bool isPaused(); - - void handleConsoleAssert(); - - void schedulePauseOnNextStatement(InspectorDebuggerFrontendDispatcher::Reason::Enum breakReason, PassRefPtr<InspectorObject> data); + void didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) final; + void willDestroyFrontendAndBackend(DisconnectReason) final; + + void enable(ErrorString&) final; + void disable(ErrorString&) final; + void setAsyncStackTraceDepth(ErrorString&, int depth) final; + void setBreakpointsActive(ErrorString&, bool active) final; + void setBreakpointByUrl(ErrorString&, int lineNumber, const String* const optionalURL, const String* const optionalURLRegex, const int* const optionalColumnNumber, const Inspector::InspectorObject* options, Inspector::Protocol::Debugger::BreakpointId*, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Debugger::Location>>& locations) final; + void setBreakpoint(ErrorString&, const Inspector::InspectorObject& location, const Inspector::InspectorObject* options, Inspector::Protocol::Debugger::BreakpointId*, RefPtr<Inspector::Protocol::Debugger::Location>& actualLocation) final; + void removeBreakpoint(ErrorString&, const String& breakpointIdentifier) final; + void continueUntilNextRunLoop(ErrorString&) final; + void continueToLocation(ErrorString&, const InspectorObject& location) final; + void searchInContent(ErrorString&, const String& scriptID, const String& query, const bool* const optionalCaseSensitive, const bool* const optionalIsRegex, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::GenericTypes::SearchMatch>>&) final; + void getScriptSource(ErrorString&, const String& scriptID, String* scriptSource) final; + void getFunctionDetails(ErrorString&, const String& functionId, RefPtr<Inspector::Protocol::Debugger::FunctionDetails>&) final; + void pause(ErrorString&) final; + void resume(ErrorString&) final; + void stepOver(ErrorString&) final; + void stepInto(ErrorString&) final; + void stepOut(ErrorString&) final; + void setPauseOnExceptions(ErrorString&, const String& pauseState) final; + void setPauseOnAssertions(ErrorString&, bool enabled) final; + void evaluateOnCallFrame(ErrorString&, const String& callFrameId, const String& expression, const String* const objectGroup, const bool* const includeCommandLineAPI, const bool* const doNotPauseOnExceptionsAndMuteConsole, const bool* const returnByValue, const bool* const generatePreview, const bool* const saveResult, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result, Inspector::Protocol::OptOutput<bool>* wasThrown, Inspector::Protocol::OptOutput<int>* savedResultIndex) final; + void setOverlayMessage(ErrorString&, const String* const) override; + + bool isPaused() const; + bool breakpointsActive() const; + + void setSuppressAllPauses(bool); + + void handleConsoleAssert(const String& message); + + void didScheduleAsyncCall(JSC::ExecState*, int asyncCallType, int callbackIdentifier, bool singleShot); + void didCancelAsyncCall(int asyncCallType, int callbackIdentifier); + void willDispatchAsyncCall(int asyncCallType, int callbackIdentifier); + void didDispatchAsyncCall(); + + void schedulePauseOnNextStatement(DebuggerFrontendDispatcher::Reason breakReason, RefPtr<InspectorObject>&& data); void cancelPauseOnNextStatement(); - void breakProgram(InspectorDebuggerFrontendDispatcher::Reason::Enum breakReason, PassRefPtr<InspectorObject> data); + bool pauseOnNextStatementEnabled() const { return m_javaScriptPauseScheduled; } + + void breakProgram(DebuggerFrontendDispatcher::Reason breakReason, RefPtr<InspectorObject>&& data); void scriptExecutionBlockedByCSP(const String& directiveText); class Listener { @@ -101,73 +109,91 @@ public: virtual ~Listener() { } virtual void debuggerWasEnabled() = 0; virtual void debuggerWasDisabled() = 0; - virtual void stepInto() = 0; - virtual void didPause() = 0; }; void setListener(Listener* listener) { m_listener = listener; } - virtual ScriptDebugServer& scriptDebugServer() = 0; - protected: - InspectorDebuggerAgent(InjectedScriptManager*); + InspectorDebuggerAgent(AgentContext&); - InjectedScriptManager* injectedScriptManager() const { return m_injectedScriptManager; } - virtual InjectedScript injectedScriptForEval(ErrorString*, const int* executionContextId) = 0; + InjectedScriptManager& injectedScriptManager() const { return m_injectedScriptManager; } + virtual InjectedScript injectedScriptForEval(ErrorString&, const int* executionContextId) = 0; + + ScriptDebugServer& scriptDebugServer() { return m_scriptDebugServer; } - virtual void startListeningScriptDebugServer() = 0; - virtual void stopListeningScriptDebugServer(bool skipRecompile) = 0; virtual void muteConsole() = 0; virtual void unmuteConsole() = 0; virtual void enable(); virtual void disable(bool skipRecompile); - virtual void didPause(JSC::ExecState*, const Deprecated::ScriptValue& callFrames, const Deprecated::ScriptValue& exception) override; - virtual void didContinue() override; + void didPause(JSC::ExecState&, JSC::JSValue callFrames, JSC::JSValue exceptionOrCaughtValue) final; + void didContinue() final; virtual String sourceMapURLForScript(const Script&); void didClearGlobalObject(); private: - PassRefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Debugger::CallFrame>> currentCallFrames(); + Ref<Inspector::Protocol::Array<Inspector::Protocol::Debugger::CallFrame>> currentCallFrames(const InjectedScript&); + + void didParseSource(JSC::SourceID, const Script&) final; + void failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage) final; - virtual void didParseSource(JSC::SourceID, const Script&) override final; - virtual void failedToParseSource(const String& url, const String& data, int firstLine, int errorLine, const String& errorMessage) override final; - virtual void didSampleProbe(JSC::ExecState*, int probeIdentifier, int hitCount, const Deprecated::ScriptValue& sample) override final; + void breakpointActionSound(int breakpointActionIdentifier) final; + void breakpointActionProbe(JSC::ExecState&, const ScriptBreakpointAction&, unsigned batchId, unsigned sampleId, JSC::JSValue sample) final; - virtual void breakpointActionSound() override; + void resolveBreakpoint(const Script&, JSC::Breakpoint&); + void setBreakpoint(JSC::Breakpoint&, bool& existing); + void didSetBreakpoint(const JSC::Breakpoint&, const String&, const ScriptBreakpoint&); - PassRefPtr<Inspector::TypeBuilder::Debugger::Location> resolveBreakpoint(const String& breakpointIdentifier, JSC::SourceID, const ScriptBreakpoint&); - bool assertPaused(ErrorString*); - void clearResolvedBreakpointState(); + bool assertPaused(ErrorString&); + void clearDebuggerBreakpointState(); + void clearInspectorBreakpointState(); void clearBreakDetails(); + void clearExceptionValue(); + void clearAsyncStackTraceData(); + + enum class ShouldDispatchResumed { No, WhenIdle, WhenContinued }; + void registerIdleHandler(); + void willStepAndMayBecomeIdle(); + void didBecomeIdle(); - bool breakpointActionsFromProtocol(ErrorString*, RefPtr<InspectorArray>& actions, Vector<ScriptBreakpointAction>* result); + RefPtr<InspectorObject> buildBreakpointPauseReason(JSC::BreakpointID); + RefPtr<InspectorObject> buildExceptionPauseReason(JSC::JSValue exception, const InjectedScript&); + + bool breakpointActionsFromProtocol(ErrorString&, RefPtr<InspectorArray>& actions, BreakpointActions* result); + + typedef std::pair<int, int> AsyncCallIdentifier; typedef HashMap<JSC::SourceID, Script> ScriptsMap; typedef HashMap<String, Vector<JSC::BreakpointID>> BreakpointIdentifierToDebugServerBreakpointIDsMap; typedef HashMap<String, RefPtr<InspectorObject>> BreakpointIdentifierToBreakpointMap; - - InjectedScriptManager* m_injectedScriptManager; - std::unique_ptr<InspectorDebuggerFrontendDispatcher> m_frontendDispatcher; - RefPtr<InspectorDebuggerBackendDispatcher> m_backendDispatcher; - Listener* m_listener; - JSC::ExecState* m_pausedScriptState; + typedef HashMap<JSC::BreakpointID, String> DebugServerBreakpointIDToBreakpointIdentifier; + + InjectedScriptManager& m_injectedScriptManager; + std::unique_ptr<DebuggerFrontendDispatcher> m_frontendDispatcher; + RefPtr<DebuggerBackendDispatcher> m_backendDispatcher; + ScriptDebugServer& m_scriptDebugServer; + Listener* m_listener { nullptr }; + JSC::ExecState* m_pausedScriptState { nullptr }; Deprecated::ScriptValue m_currentCallStack; ScriptsMap m_scripts; BreakpointIdentifierToDebugServerBreakpointIDsMap m_breakpointIdentifierToDebugServerBreakpointIDs; BreakpointIdentifierToBreakpointMap m_javaScriptBreakpoints; + DebugServerBreakpointIDToBreakpointIdentifier m_debuggerBreakpointIdentifierToInspectorBreakpointIdentifier; JSC::BreakpointID m_continueToLocationBreakpointID; - InspectorDebuggerFrontendDispatcher::Reason::Enum m_breakReason; + DebuggerFrontendDispatcher::Reason m_breakReason; RefPtr<InspectorObject> m_breakAuxData; - bool m_enabled; - bool m_javaScriptPauseScheduled; - int m_nextProbeSampleId; - int m_nextBreakpointActionIdentifier; + ShouldDispatchResumed m_conditionToDispatchResumed { ShouldDispatchResumed::No }; + bool m_enablePauseWhenIdle { false }; + HashMap<AsyncCallIdentifier, RefPtr<AsyncStackTrace>> m_pendingAsyncCalls; + std::optional<AsyncCallIdentifier> m_currentAsyncCallIdentifier { std::nullopt }; + bool m_enabled { false }; + bool m_javaScriptPauseScheduled { false }; + bool m_hasExceptionValue { false }; + bool m_didPauseStopwatch { false }; + bool m_pauseOnAssertionFailures { false }; + bool m_registeredIdleCallback { false }; + int m_asyncStackTraceDepth { 0 }; }; } // namespace Inspector - -#endif // ENABLE(INSPECTOR) - -#endif // !defined(InspectorDebuggerAgent_h) diff --git a/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.cpp new file mode 100644 index 000000000..b33ae982d --- /dev/null +++ b/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.cpp @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InspectorHeapAgent.h" + +#include "HeapProfiler.h" +#include "InjectedScript.h" +#include "InjectedScriptManager.h" +#include "InspectorEnvironment.h" +#include "JSCInlines.h" +#include "VM.h" +#include <wtf/Stopwatch.h> + +using namespace JSC; + +namespace Inspector { + +InspectorHeapAgent::InspectorHeapAgent(AgentContext& context) + : InspectorAgentBase(ASCIILiteral("Heap")) + , m_injectedScriptManager(context.injectedScriptManager) + , m_frontendDispatcher(std::make_unique<HeapFrontendDispatcher>(context.frontendRouter)) + , m_backendDispatcher(HeapBackendDispatcher::create(context.backendDispatcher, this)) + , m_environment(context.environment) +{ +} + +InspectorHeapAgent::~InspectorHeapAgent() +{ +} + +void InspectorHeapAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) +{ +} + +void InspectorHeapAgent::willDestroyFrontendAndBackend(DisconnectReason) +{ + // Stop tracking without taking a snapshot. + m_tracking = false; + + ErrorString ignored; + disable(ignored); +} + +void InspectorHeapAgent::enable(ErrorString&) +{ + if (m_enabled) + return; + + m_enabled = true; + + m_environment.vm().heap.addObserver(this); +} + +void InspectorHeapAgent::disable(ErrorString&) +{ + if (!m_enabled) + return; + + m_enabled = false; + + m_environment.vm().heap.removeObserver(this); + + clearHeapSnapshots(); +} + +void InspectorHeapAgent::gc(ErrorString&) +{ + VM& vm = m_environment.vm(); + JSLockHolder lock(vm); + sanitizeStackForVM(&vm); + vm.heap.collectAllGarbage(); +} + +void InspectorHeapAgent::snapshot(ErrorString&, double* timestamp, String* snapshotData) +{ + VM& vm = m_environment.vm(); + JSLockHolder lock(vm); + + HeapSnapshotBuilder snapshotBuilder(vm.ensureHeapProfiler()); + snapshotBuilder.buildSnapshot(); + + *timestamp = m_environment.executionStopwatch()->elapsedTime(); + *snapshotData = snapshotBuilder.json([&] (const HeapSnapshotNode& node) { + if (Structure* structure = node.cell->structure(vm)) { + if (JSGlobalObject* globalObject = structure->globalObject()) { + if (!m_environment.canAccessInspectedScriptState(globalObject->globalExec())) + return false; + } + } + return true; + }); +} + +void InspectorHeapAgent::startTracking(ErrorString& errorString) +{ + if (m_tracking) + return; + + m_tracking = true; + + double timestamp; + String snapshotData; + snapshot(errorString, ×tamp, &snapshotData); + + m_frontendDispatcher->trackingStart(timestamp, snapshotData); +} + +void InspectorHeapAgent::stopTracking(ErrorString& errorString) +{ + if (!m_tracking) + return; + + m_tracking = false; + + double timestamp; + String snapshotData; + snapshot(errorString, ×tamp, &snapshotData); + + m_frontendDispatcher->trackingComplete(timestamp, snapshotData); +} + +std::optional<HeapSnapshotNode> InspectorHeapAgent::nodeForHeapObjectIdentifier(ErrorString& errorString, unsigned heapObjectIdentifier) +{ + HeapProfiler* heapProfiler = m_environment.vm().heapProfiler(); + if (!heapProfiler) { + errorString = ASCIILiteral("No heap snapshot"); + return std::nullopt; + } + + HeapSnapshot* snapshot = heapProfiler->mostRecentSnapshot(); + if (!snapshot) { + errorString = ASCIILiteral("No heap snapshot"); + return std::nullopt; + } + + const std::optional<HeapSnapshotNode> optionalNode = snapshot->nodeForObjectIdentifier(heapObjectIdentifier); + if (!optionalNode) { + errorString = ASCIILiteral("No object for identifier, it may have been collected"); + return std::nullopt; + } + + return optionalNode; +} + +void InspectorHeapAgent::getPreview(ErrorString& errorString, int heapObjectId, Inspector::Protocol::OptOutput<String>* resultString, RefPtr<Inspector::Protocol::Debugger::FunctionDetails>& functionDetails, RefPtr<Inspector::Protocol::Runtime::ObjectPreview>& objectPreview) +{ + // Prevent the cell from getting collected as we look it up. + VM& vm = m_environment.vm(); + JSLockHolder lock(vm); + DeferGC deferGC(vm.heap); + + unsigned heapObjectIdentifier = static_cast<unsigned>(heapObjectId); + const std::optional<HeapSnapshotNode> optionalNode = nodeForHeapObjectIdentifier(errorString, heapObjectIdentifier); + if (!optionalNode) + return; + + // String preview. + JSCell* cell = optionalNode->cell; + if (cell->isString()) { + *resultString = asString(cell)->tryGetValue(); + return; + } + + // FIXME: Provide preview information for Internal Objects? CodeBlock, Executable, etc. + + Structure* structure = cell->structure(vm); + if (!structure) { + errorString = ASCIILiteral("Unable to get object details - Structure"); + return; + } + + JSGlobalObject* globalObject = structure->globalObject(); + if (!globalObject) { + errorString = ASCIILiteral("Unable to get object details - GlobalObject"); + return; + } + + InjectedScript injectedScript = m_injectedScriptManager.injectedScriptFor(globalObject->globalExec()); + if (injectedScript.hasNoValue()) { + errorString = ASCIILiteral("Unable to get object details - InjectedScript"); + return; + } + + // Function preview. + if (cell->inherits(vm, JSFunction::info())) { + injectedScript.functionDetails(errorString, cell, &functionDetails); + return; + } + + // Object preview. + objectPreview = injectedScript.previewValue(cell); +} + +void InspectorHeapAgent::getRemoteObject(ErrorString& errorString, int heapObjectId, const String* const optionalObjectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result) +{ + // Prevent the cell from getting collected as we look it up. + VM& vm = m_environment.vm(); + JSLockHolder lock(vm); + DeferGC deferGC(vm.heap); + + unsigned heapObjectIdentifier = static_cast<unsigned>(heapObjectId); + const std::optional<HeapSnapshotNode> optionalNode = nodeForHeapObjectIdentifier(errorString, heapObjectIdentifier); + if (!optionalNode) + return; + + JSCell* cell = optionalNode->cell; + Structure* structure = cell->structure(vm); + if (!structure) { + errorString = ASCIILiteral("Unable to get object details"); + return; + } + + JSGlobalObject* globalObject = structure->globalObject(); + if (!globalObject) { + errorString = ASCIILiteral("Unable to get object details"); + return; + } + + InjectedScript injectedScript = m_injectedScriptManager.injectedScriptFor(globalObject->globalExec()); + if (injectedScript.hasNoValue()) { + errorString = ASCIILiteral("Unable to get object details - InjectedScript"); + return; + } + + String objectGroup = optionalObjectGroup ? *optionalObjectGroup : String(); + result = injectedScript.wrapObject(cell, objectGroup, true); +} + +static Inspector::Protocol::Heap::GarbageCollection::Type protocolTypeForHeapOperation(CollectionScope scope) +{ + switch (scope) { + case CollectionScope::Full: + return Inspector::Protocol::Heap::GarbageCollection::Type::Full; + case CollectionScope::Eden: + return Inspector::Protocol::Heap::GarbageCollection::Type::Partial; + } + ASSERT_NOT_REACHED(); + return Inspector::Protocol::Heap::GarbageCollection::Type::Full; +} + +void InspectorHeapAgent::willGarbageCollect() +{ + if (!m_enabled) + return; + + m_gcStartTime = m_environment.executionStopwatch()->elapsedTime(); +} + +void InspectorHeapAgent::didGarbageCollect(CollectionScope scope) +{ + if (!m_enabled) { + m_gcStartTime = NAN; + return; + } + + if (std::isnan(m_gcStartTime)) { + // We were not enabled when the GC began. + return; + } + + // FIXME: Include number of bytes freed by collection. + + double endTime = m_environment.executionStopwatch()->elapsedTime(); + dispatchGarbageCollectedEvent(protocolTypeForHeapOperation(scope), m_gcStartTime, endTime); + + m_gcStartTime = NAN; +} + +void InspectorHeapAgent::clearHeapSnapshots() +{ + VM& vm = m_environment.vm(); + JSLockHolder lock(vm); + + if (HeapProfiler* heapProfiler = vm.heapProfiler()) { + heapProfiler->clearSnapshots(); + HeapSnapshotBuilder::resetNextAvailableObjectIdentifier(); + } +} + +void InspectorHeapAgent::dispatchGarbageCollectedEvent(Inspector::Protocol::Heap::GarbageCollection::Type type, double startTime, double endTime) +{ + auto protocolObject = Inspector::Protocol::Heap::GarbageCollection::create() + .setType(type) + .setStartTime(startTime) + .setEndTime(endTime) + .release(); + + m_frontendDispatcher->garbageCollected(WTFMove(protocolObject)); +} + +} // namespace Inspector diff --git a/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.h b/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.h new file mode 100644 index 000000000..421566a04 --- /dev/null +++ b/Source/JavaScriptCore/inspector/agents/InspectorHeapAgent.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "HeapSnapshot.h" +#include "InspectorBackendDispatchers.h" +#include "InspectorFrontendDispatchers.h" +#include "heap/HeapObserver.h" +#include "inspector/InspectorAgentBase.h" +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> + +namespace Inspector { + +class InjectedScriptManager; +typedef String ErrorString; + +class JS_EXPORT_PRIVATE InspectorHeapAgent : public InspectorAgentBase, public HeapBackendDispatcherHandler, public JSC::HeapObserver { + WTF_MAKE_NONCOPYABLE(InspectorHeapAgent); +public: + InspectorHeapAgent(AgentContext&); + virtual ~InspectorHeapAgent(); + + void didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) override; + void willDestroyFrontendAndBackend(DisconnectReason) override; + + // HeapBackendDispatcherHandler + void enable(ErrorString&) override; + void disable(ErrorString&) override; + void gc(ErrorString&) final; + void snapshot(ErrorString&, double* timestamp, String* snapshotData) final; + void startTracking(ErrorString&) final; + void stopTracking(ErrorString&) final; + void getPreview(ErrorString&, int heapObjectId, Inspector::Protocol::OptOutput<String>* resultString, RefPtr<Inspector::Protocol::Debugger::FunctionDetails>& functionDetails, RefPtr<Inspector::Protocol::Runtime::ObjectPreview>& objectPreview) final; + void getRemoteObject(ErrorString&, int heapObjectId, const String* const optionalObjectGroup, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result) final; + + // HeapObserver + void willGarbageCollect() override; + void didGarbageCollect(JSC::CollectionScope) override; + +protected: + void clearHeapSnapshots(); + + virtual void dispatchGarbageCollectedEvent(Inspector::Protocol::Heap::GarbageCollection::Type, double startTime, double endTime); + +private: + std::optional<JSC::HeapSnapshotNode> nodeForHeapObjectIdentifier(ErrorString&, unsigned heapObjectIdentifier); + + InjectedScriptManager& m_injectedScriptManager; + std::unique_ptr<HeapFrontendDispatcher> m_frontendDispatcher; + RefPtr<HeapBackendDispatcher> m_backendDispatcher; + InspectorEnvironment& m_environment; + + bool m_enabled { false }; + bool m_tracking { false }; + double m_gcStartTime { NAN }; +}; + +} // namespace Inspector diff --git a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp index 76a47fb3a..d42c8968c 100644 --- a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp +++ b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2013-2015 Apple Inc. All rights reserved. * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,17 +32,20 @@ #include "config.h" #include "InspectorRuntimeAgent.h" -#if ENABLE(INSPECTOR) - #include "Completion.h" +#include "DFGWorklist.h" +#include "HeapIterationScope.h" #include "InjectedScript.h" #include "InjectedScriptManager.h" +#include "InspectorFrontendRouter.h" #include "InspectorValues.h" #include "JSLock.h" #include "ParserError.h" #include "ScriptDebugServer.h" #include "SourceCode.h" -#include <wtf/PassRefPtr.h> +#include "TypeProfiler.h" +#include "TypeProfilerLog.h" +#include <wtf/CurrentTime.h> using namespace JSC; @@ -53,11 +56,11 @@ static bool asBool(const bool* const b) return b ? *b : false; } -InspectorRuntimeAgent::InspectorRuntimeAgent(InjectedScriptManager* injectedScriptManager) +InspectorRuntimeAgent::InspectorRuntimeAgent(AgentContext& context) : InspectorAgentBase(ASCIILiteral("Runtime")) - , m_injectedScriptManager(injectedScriptManager) - , m_scriptDebugServer(nullptr) - , m_enabled(false) + , m_injectedScriptManager(context.injectedScriptManager) + , m_scriptDebugServer(context.environment.scriptDebugServer()) + , m_vm(context.environment.vm()) { } @@ -65,53 +68,51 @@ InspectorRuntimeAgent::~InspectorRuntimeAgent() { } -static ScriptDebugServer::PauseOnExceptionsState setPauseOnExceptionsState(ScriptDebugServer* scriptDebugServer, ScriptDebugServer::PauseOnExceptionsState newState) +static ScriptDebugServer::PauseOnExceptionsState setPauseOnExceptionsState(ScriptDebugServer& scriptDebugServer, ScriptDebugServer::PauseOnExceptionsState newState) { - ASSERT(scriptDebugServer); - ScriptDebugServer::PauseOnExceptionsState presentState = scriptDebugServer->pauseOnExceptionsState(); + ScriptDebugServer::PauseOnExceptionsState presentState = scriptDebugServer.pauseOnExceptionsState(); if (presentState != newState) - scriptDebugServer->setPauseOnExceptionsState(newState); + scriptDebugServer.setPauseOnExceptionsState(newState); return presentState; } -static PassRefPtr<Inspector::TypeBuilder::Runtime::ErrorRange> buildErrorRangeObject(const JSTokenLocation& tokenLocation) +static Ref<Inspector::Protocol::Runtime::ErrorRange> buildErrorRangeObject(const JSTokenLocation& tokenLocation) { - RefPtr<Inspector::TypeBuilder::Runtime::ErrorRange> result = Inspector::TypeBuilder::Runtime::ErrorRange::create() + return Inspector::Protocol::Runtime::ErrorRange::create() .setStartOffset(tokenLocation.startOffset) - .setEndOffset(tokenLocation.endOffset); - return result.release(); + .setEndOffset(tokenLocation.endOffset) + .release(); } -void InspectorRuntimeAgent::parse(ErrorString*, const String& expression, Inspector::TypeBuilder::Runtime::SyntaxErrorType::Enum* result, Inspector::TypeBuilder::OptOutput<String>* message, RefPtr<Inspector::TypeBuilder::Runtime::ErrorRange>& range) +void InspectorRuntimeAgent::parse(ErrorString&, const String& expression, Inspector::Protocol::Runtime::SyntaxErrorType* result, Inspector::Protocol::OptOutput<String>* message, RefPtr<Inspector::Protocol::Runtime::ErrorRange>& range) { - VM* vm = globalVM(); - JSLockHolder lock(vm); + JSLockHolder lock(m_vm); ParserError error; - checkSyntax(*vm, JSC::makeSource(expression), error); + checkSyntax(m_vm, JSC::makeSource(expression, { }), error); - switch (error.m_syntaxErrorType) { + switch (error.syntaxErrorType()) { case ParserError::SyntaxErrorNone: - *result = Inspector::TypeBuilder::Runtime::SyntaxErrorType::None; + *result = Inspector::Protocol::Runtime::SyntaxErrorType::None; break; case ParserError::SyntaxErrorIrrecoverable: - *result = Inspector::TypeBuilder::Runtime::SyntaxErrorType::Irrecoverable; + *result = Inspector::Protocol::Runtime::SyntaxErrorType::Irrecoverable; break; case ParserError::SyntaxErrorUnterminatedLiteral: - *result = Inspector::TypeBuilder::Runtime::SyntaxErrorType::UnterminatedLiteral; + *result = Inspector::Protocol::Runtime::SyntaxErrorType::UnterminatedLiteral; break; case ParserError::SyntaxErrorRecoverable: - *result = Inspector::TypeBuilder::Runtime::SyntaxErrorType::Recoverable; + *result = Inspector::Protocol::Runtime::SyntaxErrorType::Recoverable; break; } - if (error.m_syntaxErrorType != ParserError::SyntaxErrorNone) { - *message = error.m_message; - range = buildErrorRangeObject(error.m_token.m_location); + if (error.syntaxErrorType() != ParserError::SyntaxErrorNone) { + *message = error.message(); + range = buildErrorRangeObject(error.token().m_location); } } -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::TypeBuilder::Runtime::RemoteObject>& result, Inspector::TypeBuilder::OptOutput<bool>* wasThrown) +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) { InjectedScript injectedScript = injectedScriptForEval(errorString, executionContextId); if (injectedScript.hasNoValue()) @@ -123,7 +124,7 @@ void InspectorRuntimeAgent::evaluate(ErrorString* errorString, const String& exp if (asBool(doNotPauseOnExceptionsAndMuteConsole)) muteConsole(); - injectedScript.evaluate(errorString, expression, objectGroup ? *objectGroup : "", asBool(includeCommandLineAPI), asBool(returnByValue), asBool(generatePreview), &result, wasThrown); + injectedScript.evaluate(errorString, expression, objectGroup ? *objectGroup : String(), asBool(includeCommandLineAPI), asBool(returnByValue), asBool(generatePreview), asBool(saveResult), &result, wasThrown, savedResultIndex); if (asBool(doNotPauseOnExceptionsAndMuteConsole)) { unmuteConsole(); @@ -131,17 +132,17 @@ void InspectorRuntimeAgent::evaluate(ErrorString* errorString, const String& exp } } -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::TypeBuilder::Runtime::RemoteObject>& result, Inspector::TypeBuilder::OptOutput<bool>* wasThrown) +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) { - InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId); + InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId); if (injectedScript.hasNoValue()) { - *errorString = ASCIILiteral("Inspected frame has gone"); + errorString = ASCIILiteral("Could not find InjectedScript for objectId"); return; } String arguments; if (optionalArguments) - arguments = (*optionalArguments)->toJSONString(); + arguments = optionalArguments->toJSONString(); ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = ScriptDebugServer::DontPauseOnExceptions; if (asBool(doNotPauseOnExceptionsAndMuteConsole)) @@ -157,40 +158,224 @@ void InspectorRuntimeAgent::callFunctionOn(ErrorString* errorString, const Strin } } -void InspectorRuntimeAgent::getProperties(ErrorString* errorString, const String& objectId, const bool* const ownProperties, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Runtime::PropertyDescriptor>>& result, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Runtime::InternalPropertyDescriptor>>& internalProperties) +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) { - InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId); + InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId); if (injectedScript.hasNoValue()) { - *errorString = ASCIILiteral("Inspected frame has gone"); + errorString = ASCIILiteral("Could not find InjectedScript for objectId"); return; } ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions); muteConsole(); - injectedScript.getProperties(errorString, objectId, ownProperties ? *ownProperties : false, &result); - injectedScript.getInternalProperties(errorString, objectId, &internalProperties); + injectedScript.getProperties(errorString, objectId, asBool(ownProperties), asBool(generatePreview), &result); + injectedScript.getInternalProperties(errorString, objectId, asBool(generatePreview), &internalProperties); unmuteConsole(); setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState); } -void InspectorRuntimeAgent::releaseObject(ErrorString*, const String& objectId) +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) { - InjectedScript injectedScript = m_injectedScriptManager->injectedScriptForObjectId(objectId); + InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId); + if (injectedScript.hasNoValue()) { + errorString = ASCIILiteral("Could not find InjectedScript for objectId"); + return; + } + + ScriptDebugServer::PauseOnExceptionsState previousPauseOnExceptionsState = setPauseOnExceptionsState(m_scriptDebugServer, ScriptDebugServer::DontPauseOnExceptions); + muteConsole(); + + injectedScript.getDisplayableProperties(errorString, objectId, asBool(generatePreview), &result); + injectedScript.getInternalProperties(errorString, objectId, asBool(generatePreview), &internalProperties); + + unmuteConsole(); + setPauseOnExceptionsState(m_scriptDebugServer, previousPauseOnExceptionsState); +} + +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) +{ + InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId); + if (injectedScript.hasNoValue()) { + errorString = ASCIILiteral("Could not find InjectedScript for objectId"); + return; + } + + int start = startIndex && *startIndex >= 0 ? *startIndex : 0; + int fetch = numberToFetch && *numberToFetch >= 0 ? *numberToFetch : 0; + + injectedScript.getCollectionEntries(errorString, objectId, objectGroup ? *objectGroup : String(), start, fetch, &entries); +} + +void InspectorRuntimeAgent::saveResult(ErrorString& errorString, const Inspector::InspectorObject& callArgument, const int* executionContextId, Inspector::Protocol::OptOutput<int>* savedResultIndex) +{ + InjectedScript injectedScript; + + String objectId; + if (callArgument.getString(ASCIILiteral("objectId"), objectId)) { + injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId); + if (injectedScript.hasNoValue()) { + errorString = ASCIILiteral("Could not find InjectedScript for objectId"); + return; + } + } else { + injectedScript = injectedScriptForEval(errorString, executionContextId); + if (injectedScript.hasNoValue()) + return; + } + + injectedScript.saveResult(errorString, callArgument.toJSONString(), savedResultIndex); +} + +void InspectorRuntimeAgent::releaseObject(ErrorString&, const String& objectId) +{ + InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId); if (!injectedScript.hasNoValue()) injectedScript.releaseObject(objectId); } -void InspectorRuntimeAgent::releaseObjectGroup(ErrorString*, const String& objectGroup) +void InspectorRuntimeAgent::releaseObjectGroup(ErrorString&, const String& objectGroup) +{ + m_injectedScriptManager.releaseObjectGroup(objectGroup); +} + +void InspectorRuntimeAgent::getRuntimeTypesForVariablesAtOffsets(ErrorString& errorString, const Inspector::InspectorArray& locations, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::TypeDescription>>& typeDescriptions) +{ + static const bool verbose = false; + + typeDescriptions = Inspector::Protocol::Array<Inspector::Protocol::Runtime::TypeDescription>::create(); + if (!m_vm.typeProfiler()) { + errorString = ASCIILiteral("The VM does not currently have Type Information."); + return; + } + + double start = currentTimeMS(); + m_vm.typeProfilerLog()->processLogEntries(ASCIILiteral("User Query")); + + for (size_t i = 0; i < locations.length(); i++) { + RefPtr<Inspector::InspectorValue> value = locations.get(i); + RefPtr<InspectorObject> location; + if (!value->asObject(location)) { + errorString = ASCIILiteral("Array of TypeLocation objects has an object that does not have type of TypeLocation."); + return; + } + + int descriptor; + String sourceIDAsString; + int divot; + location->getInteger(ASCIILiteral("typeInformationDescriptor"), descriptor); + location->getString(ASCIILiteral("sourceID"), sourceIDAsString); + location->getInteger(ASCIILiteral("divot"), divot); + + bool okay; + TypeLocation* typeLocation = m_vm.typeProfiler()->findLocation(divot, sourceIDAsString.toIntPtrStrict(&okay), static_cast<TypeProfilerSearchDescriptor>(descriptor), m_vm); + ASSERT(okay); + + RefPtr<TypeSet> typeSet; + if (typeLocation) { + if (typeLocation->m_globalTypeSet && typeLocation->m_globalVariableID != TypeProfilerNoGlobalIDExists) + typeSet = typeLocation->m_globalTypeSet; + else + typeSet = typeLocation->m_instructionTypeSet; + } + + bool isValid = typeLocation && typeSet && !typeSet->isEmpty(); + auto description = Inspector::Protocol::Runtime::TypeDescription::create() + .setIsValid(isValid) + .release(); + + if (isValid) { + description->setLeastCommonAncestor(typeSet->leastCommonAncestor()); + description->setStructures(typeSet->allStructureRepresentations()); + description->setTypeSet(typeSet->inspectorTypeSet()); + description->setIsTruncated(typeSet->isOverflown()); + } + + typeDescriptions->addItem(WTFMove(description)); + } + + double end = currentTimeMS(); + if (verbose) + dataLogF("Inspector::getRuntimeTypesForVariablesAtOffsets took %lfms\n", end - start); +} + +void InspectorRuntimeAgent::willDestroyFrontendAndBackend(DisconnectReason reason) { - m_injectedScriptManager->releaseObjectGroup(objectGroup); + if (reason != DisconnectReason::InspectedTargetDestroyed && m_isTypeProfilingEnabled) + setTypeProfilerEnabledState(false); } -void InspectorRuntimeAgent::run(ErrorString*) +void InspectorRuntimeAgent::enableTypeProfiler(ErrorString&) { + setTypeProfilerEnabledState(true); } -} // namespace Inspector +void InspectorRuntimeAgent::disableTypeProfiler(ErrorString&) +{ + setTypeProfilerEnabledState(false); +} -#endif // ENABLE(INSPECTOR) +void InspectorRuntimeAgent::enableControlFlowProfiler(ErrorString&) +{ + setControlFlowProfilerEnabledState(true); +} + +void InspectorRuntimeAgent::disableControlFlowProfiler(ErrorString&) +{ + setControlFlowProfilerEnabledState(false); +} + +void InspectorRuntimeAgent::setTypeProfilerEnabledState(bool isTypeProfilingEnabled) +{ + if (m_isTypeProfilingEnabled == isTypeProfilingEnabled) + return; + m_isTypeProfilingEnabled = isTypeProfilingEnabled; + + VM& vm = m_vm; + vm.whenIdle([&vm, isTypeProfilingEnabled] () { + bool shouldRecompileFromTypeProfiler = (isTypeProfilingEnabled ? vm.enableTypeProfiler() : vm.disableTypeProfiler()); + if (shouldRecompileFromTypeProfiler) + vm.deleteAllCode(PreventCollectionAndDeleteAllCode); + }); +} + +void InspectorRuntimeAgent::setControlFlowProfilerEnabledState(bool isControlFlowProfilingEnabled) +{ + if (m_isControlFlowProfilingEnabled == isControlFlowProfilingEnabled) + return; + m_isControlFlowProfilingEnabled = isControlFlowProfilingEnabled; + + VM& vm = m_vm; + vm.whenIdle([&vm, isControlFlowProfilingEnabled] () { + bool shouldRecompileFromControlFlowProfiler = (isControlFlowProfilingEnabled ? vm.enableControlFlowProfiler() : vm.disableControlFlowProfiler()); + + if (shouldRecompileFromControlFlowProfiler) + vm.deleteAllCode(PreventCollectionAndDeleteAllCode); + }); +} + +void InspectorRuntimeAgent::getBasicBlocks(ErrorString& errorString, const String& sourceIDAsString, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::BasicBlock>>& basicBlocks) +{ + if (!m_vm.controlFlowProfiler()) { + errorString = ASCIILiteral("The VM does not currently have a Control Flow Profiler."); + return; + } + + bool okay; + intptr_t sourceID = sourceIDAsString.toIntPtrStrict(&okay); + ASSERT(okay); + const Vector<BasicBlockRange>& basicBlockRanges = m_vm.controlFlowProfiler()->getBasicBlocksForSourceID(sourceID, m_vm); + basicBlocks = Inspector::Protocol::Array<Inspector::Protocol::Runtime::BasicBlock>::create(); + for (const BasicBlockRange& block : basicBlockRanges) { + Ref<Inspector::Protocol::Runtime::BasicBlock> location = Inspector::Protocol::Runtime::BasicBlock::create() + .setStartOffset(block.m_startOffset) + .setEndOffset(block.m_endOffset) + .setHasExecuted(block.m_hasExecuted) + .setExecutionCount(block.m_executionCount) + .release(); + basicBlocks->addItem(WTFMove(location)); + } +} + +} // namespace Inspector diff --git a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h index 2bc6e2994..2d2f626ec 100644 --- a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h +++ b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013, 2015-2016 Apple Inc. All rights reserved. * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,13 +29,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef InspectorRuntimeAgent_h -#define InspectorRuntimeAgent_h +#pragma once -#if ENABLE(INSPECTOR) - -#include "InspectorJSBackendDispatchers.h" -#include "InspectorJSFrontendDispatchers.h" +#include "InspectorBackendDispatchers.h" +#include "InspectorFrontendDispatchers.h" #include "inspector/InspectorAgentBase.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> @@ -52,43 +49,53 @@ class InspectorArray; class ScriptDebugServer; typedef String ErrorString; -class JS_EXPORT_PRIVATE InspectorRuntimeAgent : public InspectorAgentBase, public InspectorRuntimeBackendDispatcherHandler { +class JS_EXPORT_PRIVATE InspectorRuntimeAgent : public InspectorAgentBase, public RuntimeBackendDispatcherHandler { WTF_MAKE_NONCOPYABLE(InspectorRuntimeAgent); public: virtual ~InspectorRuntimeAgent(); - virtual void enable(ErrorString*) override { m_enabled = true; } - virtual void disable(ErrorString*) override { m_enabled = false; } - virtual void parse(ErrorString*, const String& expression, Inspector::TypeBuilder::Runtime::SyntaxErrorType::Enum* result, Inspector::TypeBuilder::OptOutput<String>* message, RefPtr<Inspector::TypeBuilder::Runtime::ErrorRange>&) override final; - virtual void evaluate(ErrorString*, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const int* executionContextId, const bool* returnByValue, const bool* generatePreview, RefPtr<Inspector::TypeBuilder::Runtime::RemoteObject>& result, Inspector::TypeBuilder::OptOutput<bool>* wasThrown) override final; - virtual void callFunctionOn(ErrorString*, const String& objectId, const String& expression, const RefPtr<Inspector::InspectorArray>* optionalArguments, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, RefPtr<Inspector::TypeBuilder::Runtime::RemoteObject>& result, Inspector::TypeBuilder::OptOutput<bool>* wasThrown) override final; - virtual void releaseObject(ErrorString*, const ErrorString& objectId) override final; - virtual void getProperties(ErrorString*, const String& objectId, const bool* ownProperties, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Runtime::PropertyDescriptor>>& result, RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::Runtime::InternalPropertyDescriptor>>& internalProperties) override final; - virtual void releaseObjectGroup(ErrorString*, const String& objectGroup) override final; - virtual void run(ErrorString*) override; - - void setScriptDebugServer(ScriptDebugServer* scriptDebugServer) { m_scriptDebugServer = scriptDebugServer; } + void willDestroyFrontendAndBackend(DisconnectReason) override; + + void enable(ErrorString&) override { m_enabled = true; } + void disable(ErrorString&) override { m_enabled = false; } + void parse(ErrorString&, const String& expression, Inspector::Protocol::Runtime::SyntaxErrorType* result, Inspector::Protocol::OptOutput<String>* message, RefPtr<Inspector::Protocol::Runtime::ErrorRange>&) final; + void evaluate(ErrorString&, const String& expression, const String* const objectGroup, const bool* const includeCommandLineAPI, const bool* const doNotPauseOnExceptionsAndMuteConsole, const int* const executionContextId, const bool* const returnByValue, const bool* const generatePreview, const bool* const saveResult, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result, Inspector::Protocol::OptOutput<bool>* wasThrown, Inspector::Protocol::OptOutput<int>* savedResultIndex) final; + void callFunctionOn(ErrorString&, const String& objectId, const String& expression, const Inspector::InspectorArray* optionalArguments, const bool* const doNotPauseOnExceptionsAndMuteConsole, const bool* const returnByValue, const bool* const generatePreview, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result, Inspector::Protocol::OptOutput<bool>* wasThrown) final; + void releaseObject(ErrorString&, const ErrorString& objectId) final; + void getProperties(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) final; + void getDisplayableProperties(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) final; + void getCollectionEntries(ErrorString&, const String& objectId, const String* const objectGroup, const int* const startIndex, const int* const numberToFetch, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::CollectionEntry>>& entries) final; + void saveResult(ErrorString&, const Inspector::InspectorObject& callArgument, const int* const executionContextId, Inspector::Protocol::OptOutput<int>* savedResultIndex) final; + void releaseObjectGroup(ErrorString&, const String& objectGroup) final; + void getRuntimeTypesForVariablesAtOffsets(ErrorString&, const Inspector::InspectorArray& locations, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::TypeDescription>>&) override; + void enableTypeProfiler(ErrorString&) override; + void disableTypeProfiler(ErrorString&) override; + void enableControlFlowProfiler(ErrorString&) override; + void disableControlFlowProfiler(ErrorString&) override; + void getBasicBlocks(ErrorString&, const String& in_sourceID, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::Runtime::BasicBlock>>& out_basicBlocks) override; bool enabled() const { return m_enabled; } protected: - InspectorRuntimeAgent(InjectedScriptManager*); + InspectorRuntimeAgent(AgentContext&); - InjectedScriptManager* injectedScriptManager() { return m_injectedScriptManager; } + InjectedScriptManager& injectedScriptManager() { return m_injectedScriptManager; } - virtual JSC::VM* globalVM() = 0; - virtual InjectedScript injectedScriptForEval(ErrorString*, const int* executionContextId) = 0; + virtual InjectedScript injectedScriptForEval(ErrorString&, const int* executionContextId) = 0; virtual void muteConsole() = 0; virtual void unmuteConsole() = 0; private: - InjectedScriptManager* m_injectedScriptManager; - ScriptDebugServer* m_scriptDebugServer; - bool m_enabled; + void setTypeProfilerEnabledState(bool); + void setControlFlowProfilerEnabledState(bool); + + InjectedScriptManager& m_injectedScriptManager; + ScriptDebugServer& m_scriptDebugServer; + JSC::VM& m_vm; + bool m_enabled {false}; + bool m_isTypeProfilingEnabled {false}; + bool m_isControlFlowProfilingEnabled {false}; }; } // namespace Inspector - -#endif // ENABLE(INSPECTOR) -#endif // InspectorRuntimeAgent_h diff --git a/Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.cpp new file mode 100644 index 000000000..c266f4309 --- /dev/null +++ b/Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "InspectorScriptProfilerAgent.h" + +#include "DeferGC.h" +#include "HeapInlines.h" +#include "InspectorEnvironment.h" +#include "SamplingProfiler.h" +#include <wtf/Stopwatch.h> + +using namespace JSC; + +namespace Inspector { + +InspectorScriptProfilerAgent::InspectorScriptProfilerAgent(AgentContext& context) + : InspectorAgentBase(ASCIILiteral("ScriptProfiler")) + , m_frontendDispatcher(std::make_unique<ScriptProfilerFrontendDispatcher>(context.frontendRouter)) + , m_backendDispatcher(ScriptProfilerBackendDispatcher::create(context.backendDispatcher, this)) + , m_environment(context.environment) +{ +} + +InspectorScriptProfilerAgent::~InspectorScriptProfilerAgent() +{ +} + +void InspectorScriptProfilerAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) +{ +} + +void InspectorScriptProfilerAgent::willDestroyFrontendAndBackend(DisconnectReason) +{ + // Stop tracking without sending results. + if (m_tracking) { + m_tracking = false; + m_activeEvaluateScript = false; + m_environment.scriptDebugServer().setProfilingClient(nullptr); + + // Stop sampling without processing the samples. + stopSamplingWhenDisconnecting(); + } +} + +void InspectorScriptProfilerAgent::startTracking(ErrorString&, const bool* const includeSamples) +{ + if (m_tracking) + return; + + m_tracking = true; + +#if ENABLE(SAMPLING_PROFILER) + if (includeSamples && *includeSamples) { + VM& vm = m_environment.scriptDebugServer().vm(); + SamplingProfiler& samplingProfiler = vm.ensureSamplingProfiler(m_environment.executionStopwatch()); + + LockHolder locker(samplingProfiler.getLock()); + samplingProfiler.setStopWatch(locker, m_environment.executionStopwatch()); + samplingProfiler.noticeCurrentThreadAsJSCExecutionThread(locker); + samplingProfiler.start(locker); + m_enabledSamplingProfiler = true; + } +#else + UNUSED_PARAM(includeSamples); +#endif // ENABLE(SAMPLING_PROFILER) + + m_environment.scriptDebugServer().setProfilingClient(this); + + m_frontendDispatcher->trackingStart(m_environment.executionStopwatch()->elapsedTime()); +} + +void InspectorScriptProfilerAgent::stopTracking(ErrorString&) +{ + if (!m_tracking) + return; + + m_tracking = false; + m_activeEvaluateScript = false; + + m_environment.scriptDebugServer().setProfilingClient(nullptr); + + trackingComplete(); +} + +bool InspectorScriptProfilerAgent::isAlreadyProfiling() const +{ + return m_activeEvaluateScript; +} + +double InspectorScriptProfilerAgent::willEvaluateScript() +{ + m_activeEvaluateScript = true; + +#if ENABLE(SAMPLING_PROFILER) + if (m_enabledSamplingProfiler) { + SamplingProfiler* samplingProfiler = m_environment.scriptDebugServer().vm().samplingProfiler(); + RELEASE_ASSERT(samplingProfiler); + samplingProfiler->noticeCurrentThreadAsJSCExecutionThread(); + } +#endif + + return m_environment.executionStopwatch()->elapsedTime(); +} + +void InspectorScriptProfilerAgent::didEvaluateScript(double startTime, ProfilingReason reason) +{ + m_activeEvaluateScript = false; + + double endTime = m_environment.executionStopwatch()->elapsedTime(); + + addEvent(startTime, endTime, reason); +} + +static Inspector::Protocol::ScriptProfiler::EventType toProtocol(ProfilingReason reason) +{ + switch (reason) { + case ProfilingReason::API: + return Inspector::Protocol::ScriptProfiler::EventType::API; + case ProfilingReason::Microtask: + return Inspector::Protocol::ScriptProfiler::EventType::Microtask; + case ProfilingReason::Other: + return Inspector::Protocol::ScriptProfiler::EventType::Other; + } + + ASSERT_NOT_REACHED(); + return Inspector::Protocol::ScriptProfiler::EventType::Other; +} + +void InspectorScriptProfilerAgent::addEvent(double startTime, double endTime, ProfilingReason reason) +{ + ASSERT(endTime >= startTime); + + auto event = Inspector::Protocol::ScriptProfiler::Event::create() + .setStartTime(startTime) + .setEndTime(endTime) + .setType(toProtocol(reason)) + .release(); + + m_frontendDispatcher->trackingUpdate(WTFMove(event)); +} + +#if ENABLE(SAMPLING_PROFILER) +static Ref<Protocol::ScriptProfiler::Samples> buildSamples(VM& vm, Vector<SamplingProfiler::StackTrace>&& samplingProfilerStackTraces) +{ + Ref<Protocol::Array<Protocol::ScriptProfiler::StackTrace>> stackTraces = Protocol::Array<Protocol::ScriptProfiler::StackTrace>::create(); + for (SamplingProfiler::StackTrace& stackTrace : samplingProfilerStackTraces) { + Ref<Protocol::Array<Protocol::ScriptProfiler::StackFrame>> frames = Protocol::Array<Protocol::ScriptProfiler::StackFrame>::create(); + for (SamplingProfiler::StackFrame& stackFrame : stackTrace.frames) { + Ref<Protocol::ScriptProfiler::StackFrame> frame = Protocol::ScriptProfiler::StackFrame::create() + .setSourceID(String::number(stackFrame.sourceID())) + .setName(stackFrame.displayName(vm)) + .setLine(stackFrame.functionStartLine()) + .setColumn(stackFrame.functionStartColumn()) + .setUrl(stackFrame.url()) + .release(); + + if (stackFrame.hasExpressionInfo()) { + Ref<Protocol::ScriptProfiler::ExpressionLocation> expressionLocation = Protocol::ScriptProfiler::ExpressionLocation::create() + .setLine(stackFrame.lineNumber()) + .setColumn(stackFrame.columnNumber()) + .release(); + frame->setExpressionLocation(WTFMove(expressionLocation)); + } + + frames->addItem(WTFMove(frame)); + } + Ref<Protocol::ScriptProfiler::StackTrace> inspectorStackTrace = Protocol::ScriptProfiler::StackTrace::create() + .setTimestamp(stackTrace.timestamp) + .setStackFrames(WTFMove(frames)) + .release(); + stackTraces->addItem(WTFMove(inspectorStackTrace)); + } + + return Protocol::ScriptProfiler::Samples::create() + .setStackTraces(WTFMove(stackTraces)) + .release(); +} +#endif // ENABLE(SAMPLING_PROFILER) + +void InspectorScriptProfilerAgent::trackingComplete() +{ +#if ENABLE(SAMPLING_PROFILER) + if (m_enabledSamplingProfiler) { + VM& vm = m_environment.scriptDebugServer().vm(); + JSLockHolder lock(vm); + DeferGC deferGC(vm.heap); + SamplingProfiler* samplingProfiler = vm.samplingProfiler(); + RELEASE_ASSERT(samplingProfiler); + + LockHolder locker(samplingProfiler->getLock()); + samplingProfiler->pause(locker); + Vector<SamplingProfiler::StackTrace> stackTraces = samplingProfiler->releaseStackTraces(locker); + locker.unlockEarly(); + + Ref<Protocol::ScriptProfiler::Samples> samples = buildSamples(vm, WTFMove(stackTraces)); + + m_enabledSamplingProfiler = false; + + m_frontendDispatcher->trackingComplete(WTFMove(samples)); + } else + m_frontendDispatcher->trackingComplete(nullptr); +#else + m_frontendDispatcher->trackingComplete(nullptr); +#endif // ENABLE(SAMPLING_PROFILER) +} + +void InspectorScriptProfilerAgent::stopSamplingWhenDisconnecting() +{ +#if ENABLE(SAMPLING_PROFILER) + if (!m_enabledSamplingProfiler) + return; + + VM& vm = m_environment.scriptDebugServer().vm(); + JSLockHolder lock(vm); + SamplingProfiler* samplingProfiler = vm.samplingProfiler(); + RELEASE_ASSERT(samplingProfiler); + LockHolder locker(samplingProfiler->getLock()); + samplingProfiler->pause(locker); + samplingProfiler->clearData(locker); + + m_enabledSamplingProfiler = false; +#endif +} + +void InspectorScriptProfilerAgent::programmaticCaptureStarted() +{ + m_frontendDispatcher->programmaticCaptureStarted(); +} + +void InspectorScriptProfilerAgent::programmaticCaptureStopped() +{ + m_frontendDispatcher->programmaticCaptureStopped(); +} + +} // namespace Inspector diff --git a/Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.h b/Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.h new file mode 100644 index 000000000..ca1bb8a61 --- /dev/null +++ b/Source/JavaScriptCore/inspector/agents/InspectorScriptProfilerAgent.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "InspectorBackendDispatchers.h" +#include "InspectorFrontendDispatchers.h" +#include "inspector/InspectorAgentBase.h" +#include "inspector/ScriptDebugServer.h" +#include <wtf/Noncopyable.h> + +namespace JSC { +class Profile; +} + +namespace Inspector { + +typedef String ErrorString; + +class JS_EXPORT_PRIVATE InspectorScriptProfilerAgent final : public InspectorAgentBase, public ScriptProfilerBackendDispatcherHandler, public JSC::Debugger::ProfilingClient { + WTF_MAKE_NONCOPYABLE(InspectorScriptProfilerAgent); +public: + InspectorScriptProfilerAgent(AgentContext&); + virtual ~InspectorScriptProfilerAgent(); + + void didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) override; + void willDestroyFrontendAndBackend(DisconnectReason) override; + + // ScriptProfilerBackendDispatcherHandler + void startTracking(ErrorString&, const bool* const includeSamples) override; + void stopTracking(ErrorString&) override; + + void programmaticCaptureStarted(); + void programmaticCaptureStopped(); + + // Debugger::ProfilingClient + bool isAlreadyProfiling() const override; + double willEvaluateScript() override; + void didEvaluateScript(double, JSC::ProfilingReason) override; + +private: + struct Event { + Event(double start, double end) : startTime(start), endTime(end) { } + double startTime { 0 }; + double endTime { 0 }; + }; + + void addEvent(double startTime, double endTime, JSC::ProfilingReason); + void trackingComplete(); + void stopSamplingWhenDisconnecting(); + + std::unique_ptr<ScriptProfilerFrontendDispatcher> m_frontendDispatcher; + RefPtr<ScriptProfilerBackendDispatcher> m_backendDispatcher; + InspectorEnvironment& m_environment; + bool m_tracking { false }; +#if ENABLE(SAMPLING_PROFILER) + bool m_enabledSamplingProfiler { false }; +#endif + bool m_activeEvaluateScript { false }; +}; + +} // namespace Inspector diff --git a/Source/JavaScriptCore/inspector/agents/JSGlobalObjectConsoleAgent.cpp b/Source/JavaScriptCore/inspector/agents/JSGlobalObjectConsoleAgent.cpp new file mode 100644 index 000000000..401162bc2 --- /dev/null +++ b/Source/JavaScriptCore/inspector/agents/JSGlobalObjectConsoleAgent.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014, 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JSGlobalObjectConsoleAgent.h" + +namespace Inspector { + +JSGlobalObjectConsoleAgent::JSGlobalObjectConsoleAgent(AgentContext& context, InspectorHeapAgent* heapAgent) + : InspectorConsoleAgent(context, heapAgent) +{ +} + +void JSGlobalObjectConsoleAgent::setMonitoringXHREnabled(ErrorString& errorString, bool) +{ + errorString = ASCIILiteral("Not supported for JavaScript context"); +} + +void JSGlobalObjectConsoleAgent::addInspectedNode(ErrorString& errorString, int) +{ + errorString = ASCIILiteral("Not supported for JavaScript context"); +} + +} // namespace Inspector diff --git a/Source/JavaScriptCore/inspector/agents/JSGlobalObjectConsoleAgent.h b/Source/JavaScriptCore/inspector/agents/JSGlobalObjectConsoleAgent.h new file mode 100644 index 000000000..91dade3d2 --- /dev/null +++ b/Source/JavaScriptCore/inspector/agents/JSGlobalObjectConsoleAgent.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014, 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "InspectorConsoleAgent.h" +#include "JSGlobalObjectScriptDebugServer.h" + +namespace Inspector { + +class JSGlobalObjectConsoleAgent final : public InspectorConsoleAgent { + WTF_MAKE_NONCOPYABLE(JSGlobalObjectConsoleAgent); + WTF_MAKE_FAST_ALLOCATED; +public: + JSGlobalObjectConsoleAgent(AgentContext&, InspectorHeapAgent*); + virtual ~JSGlobalObjectConsoleAgent() { } + + // FIXME: XHRs and Nodes only makes sense debugging a Web context. Can this be moved to a different agent? + void setMonitoringXHREnabled(ErrorString&, bool enabled) override; + void addInspectedNode(ErrorString&, int nodeId) override; +}; + +} // namespace Inspector diff --git a/Source/JavaScriptCore/inspector/agents/JSGlobalObjectDebuggerAgent.cpp b/Source/JavaScriptCore/inspector/agents/JSGlobalObjectDebuggerAgent.cpp new file mode 100644 index 000000000..19ba13db5 --- /dev/null +++ b/Source/JavaScriptCore/inspector/agents/JSGlobalObjectDebuggerAgent.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014, 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JSGlobalObjectDebuggerAgent.h" + +#include "ConsoleMessage.h" +#include "InjectedScriptManager.h" +#include "InspectorConsoleAgent.h" +#include "JSGlobalObject.h" +#include "ScriptArguments.h" +#include "ScriptCallStack.h" +#include "ScriptCallStackFactory.h" + +using namespace JSC; + +namespace Inspector { + +JSGlobalObjectDebuggerAgent::JSGlobalObjectDebuggerAgent(JSAgentContext& context, InspectorConsoleAgent* consoleAgent) + : InspectorDebuggerAgent(context) + , m_consoleAgent(consoleAgent) +{ +} + +InjectedScript JSGlobalObjectDebuggerAgent::injectedScriptForEval(ErrorString& error, const int* executionContextId) +{ + if (executionContextId) { + error = ASCIILiteral("Execution context id is not supported for JSContext inspection as there is only one execution context."); + return InjectedScript(); + } + + ExecState* exec = static_cast<JSGlobalObjectScriptDebugServer&>(scriptDebugServer()).globalObject().globalExec(); + return injectedScriptManager().injectedScriptFor(exec); +} + +void JSGlobalObjectDebuggerAgent::breakpointActionLog(JSC::ExecState& state, const String& message) +{ + m_consoleAgent->addMessageToConsole(std::make_unique<ConsoleMessage>(MessageSource::JS, MessageType::Log, MessageLevel::Log, message, createScriptCallStack(&state, ScriptCallStack::maxCallStackSizeToCapture), 0)); +} + +} // namespace Inspector diff --git a/Source/JavaScriptCore/inspector/agents/JSGlobalObjectDebuggerAgent.h b/Source/JavaScriptCore/inspector/agents/JSGlobalObjectDebuggerAgent.h new file mode 100644 index 000000000..5476f55ce --- /dev/null +++ b/Source/JavaScriptCore/inspector/agents/JSGlobalObjectDebuggerAgent.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2014, 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "InspectorDebuggerAgent.h" +#include "JSGlobalObjectScriptDebugServer.h" + +namespace Inspector { + +class InspectorConsoleAgent; + +class JSGlobalObjectDebuggerAgent final : public InspectorDebuggerAgent { + WTF_MAKE_NONCOPYABLE(JSGlobalObjectDebuggerAgent); + WTF_MAKE_FAST_ALLOCATED; +public: + JSGlobalObjectDebuggerAgent(JSAgentContext&, InspectorConsoleAgent*); + virtual ~JSGlobalObjectDebuggerAgent() { } + + InjectedScript injectedScriptForEval(ErrorString&, const int* executionContextId) override; + + void breakpointActionLog(JSC::ExecState&, const String&) final; + + // NOTE: JavaScript inspector does not yet need to mute a console because no messages + // are sent to the console outside of the API boundary or console object. + void muteConsole() final { } + void unmuteConsole() final { } + +private: + InspectorConsoleAgent* m_consoleAgent { nullptr }; +}; + +} // namespace Inspector diff --git a/Source/JavaScriptCore/inspector/agents/JSGlobalObjectRuntimeAgent.cpp b/Source/JavaScriptCore/inspector/agents/JSGlobalObjectRuntimeAgent.cpp new file mode 100644 index 000000000..b748eeb0a --- /dev/null +++ b/Source/JavaScriptCore/inspector/agents/JSGlobalObjectRuntimeAgent.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014, 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JSGlobalObjectRuntimeAgent.h" + +#include "InjectedScript.h" +#include "InjectedScriptManager.h" +#include "JSGlobalObject.h" + +using namespace JSC; + +namespace Inspector { + +JSGlobalObjectRuntimeAgent::JSGlobalObjectRuntimeAgent(JSAgentContext& context) + : InspectorRuntimeAgent(context) + , m_frontendDispatcher(std::make_unique<RuntimeFrontendDispatcher>(context.frontendRouter)) + , m_backendDispatcher(RuntimeBackendDispatcher::create(context.backendDispatcher, this)) + , m_globalObject(context.inspectedGlobalObject) +{ +} + +void JSGlobalObjectRuntimeAgent::didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) +{ +} + +InjectedScript JSGlobalObjectRuntimeAgent::injectedScriptForEval(ErrorString& errorString, const int* executionContextId) +{ + ASSERT_UNUSED(executionContextId, !executionContextId); + + JSC::ExecState* scriptState = m_globalObject.globalExec(); + InjectedScript injectedScript = injectedScriptManager().injectedScriptFor(scriptState); + if (injectedScript.hasNoValue()) + errorString = ASCIILiteral("Internal error: main world execution context not found."); + + return injectedScript; +} + +} // namespace Inspector diff --git a/Source/JavaScriptCore/inspector/agents/JSGlobalObjectRuntimeAgent.h b/Source/JavaScriptCore/inspector/agents/JSGlobalObjectRuntimeAgent.h new file mode 100644 index 000000000..06809ad28 --- /dev/null +++ b/Source/JavaScriptCore/inspector/agents/JSGlobalObjectRuntimeAgent.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2014, 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "InspectorRuntimeAgent.h" + +namespace JSC { +class JSGlobalObject; +} + +namespace Inspector { + +class JSGlobalObjectRuntimeAgent final : public InspectorRuntimeAgent { +public: + JSGlobalObjectRuntimeAgent(JSAgentContext&); + + void didCreateFrontendAndBackend(FrontendRouter*, BackendDispatcher*) override; + + InjectedScript injectedScriptForEval(ErrorString&, const int* executionContextId) override; + + // NOTE: JavaScript inspector does not yet need to mute a console because no messages + // are sent to the console outside of the API boundary or console object. + void muteConsole() override { } + void unmuteConsole() override { } + +private: + std::unique_ptr<RuntimeFrontendDispatcher> m_frontendDispatcher; + RefPtr<RuntimeBackendDispatcher> m_backendDispatcher; + JSC::JSGlobalObject& m_globalObject; +}; + +} // namespace Inspector |