Move V4 debugger agent into the debugger plugin
authorUlf Hermann <ulf.hermann@theqtcompany.com>
Tue, 21 Jul 2015 16:56:32 +0000 (18:56 +0200)
committerUlf Hermann <ulf.hermann@theqtcompany.com>
Mon, 10 Aug 2015 10:05:09 +0000 (10:05 +0000)
The debugger is the only thing that actually needs it. Note that for
this to work we need to make QV4::Debugging::Debugger a QObject and
add some signals. The net effect is still a reduction in binary size
of about 1kb.

Change-Id: Ibecb8cfa140fc26fc13c8cbefb3d027ebdcd28a4
Reviewed-by: Erik Verbruggen <erik.verbruggen@theqtcompany.com>
src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro
src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp [new file with mode: 0644]
src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h [new file with mode: 0644]
src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
src/qml/jsruntime/qv4debugging.cpp
src/qml/jsruntime/qv4debugging_p.h
tests/auto/qml/qv4debugger/tst_qv4debugger.cpp

index 6c29411981abc0109d1c3b95e197e946ab16715d..dd0c9a174fea0004e56c3113447842e5568718ec 100644 (file)
@@ -10,7 +10,8 @@ SOURCES += \
     $$PWD/qqmldebuggerservicefactory.cpp \
     $$PWD/qqmlenginedebugservice.cpp \
     $$PWD/qqmlwatcher.cpp \
-    $$PWD/qv4debugservice.cpp
+    $$PWD/qv4debugservice.cpp \
+    $$PWD/qv4debuggeragent.cpp
 
 HEADERS += \
     $$PWD/../shared/qqmlconfigurabledebugservice.h \
@@ -18,7 +19,8 @@ HEADERS += \
     $$PWD/qqmldebuggerservicefactory.h \
     $$PWD/qqmlenginedebugservice.h \
     $$PWD/qqmlwatcher.h \
-    $$PWD/qv4debugservice.h
+    $$PWD/qv4debugservice.h \
+    $$PWD/qv4debuggeragent.h
 
 INCLUDEPATH += $$PWD \
     $$PWD/../shared
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp
new file mode 100644 (file)
index 0000000..15421f4
--- /dev/null
@@ -0,0 +1,251 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4debuggeragent.h"
+#include "qv4debugservice.h"
+
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qjsonarray.h>
+
+QT_BEGIN_NAMESPACE
+
+QV4DebuggerAgent::QV4DebuggerAgent(QV4DebugServiceImpl *debugService)
+    : m_breakOnThrow(false), m_debugService(debugService)
+{}
+
+QV4::Debugging::Debugger *QV4DebuggerAgent::firstDebugger() const
+{
+    // Currently only 1 single engine is supported, so:
+    if (m_debuggers.isEmpty())
+        return 0;
+    else
+        return m_debuggers.first();
+}
+
+bool QV4DebuggerAgent::isRunning() const
+{
+    // Currently only 1 single engine is supported, so:
+    if (QV4::Debugging::Debugger *debugger = firstDebugger())
+        return debugger->state() == QV4::Debugging::Debugger::Running;
+    else
+        return false;
+}
+
+void QV4DebuggerAgent::debuggerPaused(QV4::Debugging::Debugger *debugger,
+                                      QV4::Debugging::PauseReason reason)
+{
+    Q_UNUSED(reason);
+
+    m_debugService->clearHandles(debugger->engine());
+
+    QJsonObject event, body, script;
+    event.insert(QStringLiteral("type"), QStringLiteral("event"));
+
+    switch (reason) {
+    case QV4::Debugging::Step:
+    case QV4::Debugging::PauseRequest:
+    case QV4::Debugging::BreakPoint: {
+        event.insert(QStringLiteral("event"), QStringLiteral("break"));
+        QVector<QV4::StackFrame> frames = debugger->stackTrace(1);
+        if (frames.isEmpty())
+            break;
+
+        const QV4::StackFrame &topFrame = frames.first();
+        body.insert(QStringLiteral("invocationText"), topFrame.function);
+        body.insert(QStringLiteral("sourceLine"), topFrame.line - 1);
+        if (topFrame.column > 0)
+            body.insert(QStringLiteral("sourceColumn"), topFrame.column);
+        QJsonArray breakPoints;
+        foreach (int breakPointId, breakPointIds(topFrame.source, topFrame.line))
+            breakPoints.push_back(breakPointId);
+        body.insert(QStringLiteral("breakpoints"), breakPoints);
+        script.insert(QStringLiteral("name"), topFrame.source);
+    } break;
+    case QV4::Debugging::Throwing:
+        // TODO: complete this!
+        event.insert(QStringLiteral("event"), QStringLiteral("exception"));
+        break;
+    }
+
+    if (!script.isEmpty())
+        body.insert(QStringLiteral("script"), script);
+    if (!body.isEmpty())
+        event.insert(QStringLiteral("body"), body);
+    m_debugService->send(event);
+}
+
+void QV4DebuggerAgent::sourcesCollected(QV4::Debugging::Debugger *debugger,
+                                        const QStringList &sources, int requestSequenceNr)
+{
+    QJsonArray body;
+    foreach (const QString &source, sources) {
+        QJsonObject src;
+        src[QLatin1String("name")] = source;
+        src[QLatin1String("scriptType")] = 4;
+        body.append(src);
+    }
+
+    QJsonObject response;
+    response[QLatin1String("success")] = true;
+    response[QLatin1String("running")] = debugger->state() == QV4::Debugging::Debugger::Running;
+    response[QLatin1String("body")] = body;
+    response[QLatin1String("command")] = QStringLiteral("scripts");
+    response[QLatin1String("request_seq")] = requestSequenceNr;
+    response[QLatin1String("type")] = QStringLiteral("response");
+    m_debugService->send(response);
+}
+
+void QV4DebuggerAgent::addDebugger(QV4::Debugging::Debugger *debugger)
+{
+    Q_ASSERT(!m_debuggers.contains(debugger));
+    m_debuggers << debugger;
+
+    debugger->setBreakOnThrow(m_breakOnThrow);
+
+    foreach (const BreakPoint &breakPoint, m_breakPoints.values())
+        if (breakPoint.enabled)
+            debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition);
+
+    connect(debugger, SIGNAL(destroyed(QObject*)),
+            this, SLOT(handleDebuggerDeleted(QObject*)));
+    connect(debugger, SIGNAL(sourcesCollected(QV4::Debugging::Debugger*,QStringList,int)),
+            this, SLOT(sourcesCollected(QV4::Debugging::Debugger*,QStringList,int)),
+            Qt::QueuedConnection);
+    connect(debugger, SIGNAL(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason)),
+            this, SLOT(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason)),
+            Qt::QueuedConnection);
+}
+
+void QV4DebuggerAgent::removeDebugger(QV4::Debugging::Debugger *debugger)
+{
+    m_debuggers.removeAll(debugger);
+    disconnect(debugger, SIGNAL(destroyed(QObject*)),
+               this, SLOT(handleDebuggerDeleted(QObject*)));
+    disconnect(debugger, SIGNAL(sourcesCollected(QV4::Debugging::Debugger*,QStringList,int)),
+               this, SLOT(sourcesCollected(QV4::Debugging::Debugger*,QStringList,int)));
+    disconnect(debugger,
+               SIGNAL(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason)),
+               this,
+               SLOT(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason)));
+}
+
+void QV4DebuggerAgent::handleDebuggerDeleted(QObject *debugger)
+{
+    m_debuggers.removeAll(static_cast<QV4::Debugging::Debugger *>(debugger));
+}
+
+void QV4DebuggerAgent::pause(QV4::Debugging::Debugger *debugger) const
+{
+    debugger->pause();
+}
+
+void QV4DebuggerAgent::pauseAll() const
+{
+    foreach (QV4::Debugging::Debugger *debugger, m_debuggers)
+        pause(debugger);
+}
+
+void QV4DebuggerAgent::resumeAll() const
+{
+    foreach (QV4::Debugging::Debugger *debugger, m_debuggers)
+        if (debugger->state() == QV4::Debugging::Debugger::Paused)
+            debugger->resume(QV4::Debugging::Debugger::FullThrottle);
+}
+
+int QV4DebuggerAgent::addBreakPoint(const QString &fileName, int lineNumber, bool enabled, const QString &condition)
+{
+    if (enabled)
+        foreach (QV4::Debugging::Debugger *debugger, m_debuggers)
+            debugger->addBreakPoint(fileName, lineNumber, condition);
+
+    int id = m_breakPoints.size();
+    m_breakPoints.insert(id, BreakPoint(fileName, lineNumber, enabled, condition));
+    return id;
+}
+
+void QV4DebuggerAgent::removeBreakPoint(int id)
+{
+    BreakPoint breakPoint = m_breakPoints.value(id);
+    if (!breakPoint.isValid())
+        return;
+
+    m_breakPoints.remove(id);
+
+    if (breakPoint.enabled)
+        foreach (QV4::Debugging::Debugger *debugger, m_debuggers)
+            debugger->removeBreakPoint(breakPoint.fileName, breakPoint.lineNr);
+}
+
+void QV4DebuggerAgent::removeAllBreakPoints()
+{
+    QList<int> ids = m_breakPoints.keys();
+    foreach (int id, ids)
+        removeBreakPoint(id);
+}
+
+void QV4DebuggerAgent::enableBreakPoint(int id, bool onoff)
+{
+    BreakPoint &breakPoint = m_breakPoints[id];
+    if (!breakPoint.isValid() || breakPoint.enabled == onoff)
+        return;
+    breakPoint.enabled = onoff;
+
+    foreach (QV4::Debugging::Debugger *debugger, m_debuggers) {
+        if (onoff)
+            debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition);
+        else
+            debugger->removeBreakPoint(breakPoint.fileName, breakPoint.lineNr);
+    }
+}
+
+QList<int> QV4DebuggerAgent::breakPointIds(const QString &fileName, int lineNumber) const
+{
+    QList<int> ids;
+
+    for (QHash<int, BreakPoint>::const_iterator i = m_breakPoints.begin(), ei = m_breakPoints.end(); i != ei; ++i)
+        if (i->lineNr == lineNumber && fileName.endsWith(i->fileName))
+            ids.push_back(i.key());
+
+    return ids;
+}
+
+void QV4DebuggerAgent::setBreakOnThrow(bool onoff)
+{
+    if (onoff != m_breakOnThrow) {
+        m_breakOnThrow = onoff;
+        foreach (QV4::Debugging::Debugger *debugger, m_debuggers)
+            debugger->setBreakOnThrow(onoff);
+    }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h
new file mode 100644 (file)
index 0000000..6126eea
--- /dev/null
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4DEBUGGERAGENT_H
+#define QV4DEBUGGERAGENT_H
+
+#include <private/qv4debugging_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QV4DebugServiceImpl;
+
+class QV4DebuggerAgent : public QObject
+{
+    Q_OBJECT
+public:
+    QV4DebuggerAgent(QV4DebugServiceImpl *m_debugService);
+
+    QV4::Debugging::Debugger *firstDebugger() const;
+    bool isRunning() const;
+
+    void addDebugger(QV4::Debugging::Debugger *debugger);
+    void removeDebugger(QV4::Debugging::Debugger *debugger);
+
+    void pause(QV4::Debugging::Debugger *debugger) const;
+    void pauseAll() const;
+    void resumeAll() const;
+    int addBreakPoint(const QString &fileName, int lineNumber, bool enabled = true, const QString &condition = QString());
+    void removeBreakPoint(int id);
+    void removeAllBreakPoints();
+    void enableBreakPoint(int id, bool onoff);
+    QList<int> breakPointIds(const QString &fileName, int lineNumber) const;
+
+    bool breakOnThrow() const { return m_breakOnThrow; }
+    void setBreakOnThrow(bool onoff);
+
+public slots:
+    void debuggerPaused(QV4::Debugging::Debugger *debugger, QV4::Debugging::PauseReason reason);
+    void sourcesCollected(QV4::Debugging::Debugger *debugger, const QStringList &sources,
+                          int requestSequenceNr);
+    void handleDebuggerDeleted(QObject *debugger);
+
+private:
+    QList<QV4::Debugging::Debugger *> m_debuggers;
+
+    struct BreakPoint {
+        QString fileName;
+        int lineNr;
+        bool enabled;
+        QString condition;
+
+        BreakPoint(): lineNr(-1), enabled(false) {}
+        BreakPoint(const QString &fileName, int lineNr, bool enabled, const QString &condition)
+            : fileName(fileName), lineNr(lineNr), enabled(enabled), condition(condition)
+        {}
+
+        bool isValid() const { return lineNr >= 0 && !fileName.isEmpty(); }
+    };
+
+    QHash<int, BreakPoint> m_breakPoints;
+    bool m_breakOnThrow;
+    QV4DebugServiceImpl *m_debugService;
+};
+
+QT_END_NAMESPACE
+
+#endif // QV4DEBUGGERAGENT_H
index 62436f18c9a5506227849fb389582694e0bc982b..21bfe97808e25a1ddfb775e94cb21d71d59295ef 100644 (file)
@@ -720,90 +720,6 @@ void QV4DebugServiceImpl::sendSomethingToSomebody(const char *type, int magicNum
     emit messageToClient(name(), packMessage(type, response));
 }
 
-QV4DebuggerAgent::QV4DebuggerAgent(QV4DebugServiceImpl *debugService)
-    : debugService(debugService)
-{}
-
-QV4::Debugging::Debugger *QV4DebuggerAgent::firstDebugger() const
-{
-    // Currently only 1 single engine is supported, so:
-    if (m_debuggers.isEmpty())
-        return 0;
-    else
-        return m_debuggers.first();
-}
-
-bool QV4DebuggerAgent::isRunning() const
-{
-    // Currently only 1 single engine is supported, so:
-    if (QV4::Debugging::Debugger *debugger = firstDebugger())
-        return debugger->state() == QV4::Debugging::Debugger::Running;
-    else
-        return false;
-}
-
-void QV4DebuggerAgent::debuggerPaused(QV4::Debugging::Debugger *debugger, QV4::Debugging::PauseReason reason)
-{
-    Q_UNUSED(reason);
-
-    debugService->clearHandles(debugger->engine());
-
-    QJsonObject event, body, script;
-    event.insert(QStringLiteral("type"), QStringLiteral("event"));
-
-    switch (reason) {
-    case QV4::Debugging::Step:
-    case QV4::Debugging::PauseRequest:
-    case QV4::Debugging::BreakPoint: {
-        event.insert(QStringLiteral("event"), QStringLiteral("break"));
-        QVector<QV4::StackFrame> frames = debugger->stackTrace(1);
-        if (frames.isEmpty())
-            break;
-
-        const QV4::StackFrame &topFrame = frames.first();
-        body.insert(QStringLiteral("invocationText"), topFrame.function);
-        body.insert(QStringLiteral("sourceLine"), topFrame.line - 1);
-        if (topFrame.column > 0)
-            body.insert(QStringLiteral("sourceColumn"), topFrame.column);
-        QJsonArray breakPoints;
-        foreach (int breakPointId, breakPointIds(topFrame.source, topFrame.line))
-            breakPoints.push_back(breakPointId);
-        body.insert(QStringLiteral("breakpoints"), breakPoints);
-        script.insert(QStringLiteral("name"), topFrame.source);
-    } break;
-    case QV4::Debugging::Throwing:
-        // TODO: complete this!
-        event.insert(QStringLiteral("event"), QStringLiteral("exception"));
-        break;
-    }
-
-    if (!script.isEmpty())
-        body.insert(QStringLiteral("script"), script);
-    if (!body.isEmpty())
-        event.insert(QStringLiteral("body"), body);
-    debugService->send(event);
-}
-
-void QV4DebuggerAgent::sourcesCollected(QV4::Debugging::Debugger *debugger, QStringList sources, int requestSequenceNr)
-{
-    QJsonArray body;
-    foreach (const QString &source, sources) {
-        QJsonObject src;
-        src[QLatin1String("name")] = source;
-        src[QLatin1String("scriptType")] = 4;
-        body.append(src);
-    }
-
-    QJsonObject response;
-    response[QLatin1String("success")] = true;
-    response[QLatin1String("running")] = debugger->state() == QV4::Debugging::Debugger::Running;
-    response[QLatin1String("body")] = body;
-    response[QLatin1String("command")] = QStringLiteral("scripts");
-    response[QLatin1String("request_seq")] = requestSequenceNr;
-    response[QLatin1String("type")] = QStringLiteral("response");
-    debugService->send(response);
-}
-
 void QV4DebugServiceImpl::handleV8Request(const QByteArray &payload)
 {
     TRACE_PROTOCOL(qDebug() << "v8request, payload:" << payload.constData());
index 23bffa368a9ad11fb8471378e40be6b2693f0e8a..6e5113600d898b8c4124561754051b7de92ff336 100644 (file)
@@ -46,6 +46,7 @@
 //
 
 #include "qqmlconfigurabledebugservice.h"
+#include "qv4debuggeragent.h"
 #include <private/qqmldebugserviceinterfaces_p.h>
 #include <private/qv4debugging_p.h>
 
@@ -61,24 +62,6 @@ class V8CommandHandler;
 class UnknownV8CommandHandler;
 class QV4DebugServiceImpl;
 
-class QV4DebuggerAgent : public QV4::Debugging::DebuggerAgent
-{
-    Q_OBJECT
-public:
-    QV4DebuggerAgent(QV4DebugServiceImpl *debugService);
-    QV4::Debugging::Debugger *firstDebugger() const;
-    bool isRunning() const;
-
-public slots:
-    virtual void debuggerPaused(QV4::Debugging::Debugger *debugger,
-                                QV4::Debugging::PauseReason reason);
-    virtual void sourcesCollected(QV4::Debugging::Debugger *debugger, QStringList sources,
-                                  int requestSequenceNr);
-
-private:
-    QV4DebugServiceImpl *debugService;
-};
-
 class QV4DebugServiceImpl : public QQmlConfigurableDebugService<QV4DebugService>
 {
     Q_OBJECT
index c48bb038444582301618e4b7aeb0248b7e12dd04..6f3e48352e94f6b8b62efd5a3be2893c31b7c3c2 100644 (file)
@@ -164,10 +164,7 @@ public:
         }
 
         Debugger *debugger = engine->debugger;
-        QMetaObject::invokeMethod(debugger->agent(), "sourcesCollected", Qt::QueuedConnection,
-                                  Q_ARG(QV4::Debugging::Debugger*, debugger),
-                                  Q_ARG(QStringList, sources),
-                                  Q_ARG(int, seq));
+        emit debugger->sourcesCollected(debugger, sources, seq);
     }
 };
 }
@@ -386,7 +383,6 @@ QJsonObject DataCollector::collectAsJson(const QString &name, const ScopedValue
 
 Debugger::Debugger(QV4::ExecutionEngine *engine)
     : m_engine(engine)
-    , m_agent(0)
     , m_state(Running)
     , m_stepping(NotStepping)
     , m_pauseRequested(false)
@@ -400,29 +396,6 @@ Debugger::Debugger(QV4::ExecutionEngine *engine)
     qMetaTypeId<PauseReason>();
 }
 
-Debugger::~Debugger()
-{
-    detachFromAgent();
-}
-
-void Debugger::attachToAgent(DebuggerAgent *agent)
-{
-    Q_ASSERT(!m_agent);
-    m_agent = agent;
-}
-
-void Debugger::detachFromAgent()
-{
-    DebuggerAgent *agent = 0;
-    {
-        QMutexLocker locker(&m_lock);
-        agent = m_agent;
-        m_agent = 0;
-    }
-    if (agent)
-        agent->removeDebugger(this);
-}
-
 void Debugger::gatherSources(int requestSequenceNr)
 {
     QMutexLocker locker(&m_lock);
@@ -835,9 +808,7 @@ void Debugger::pauseAndWait(PauseReason reason)
         return;
 
     m_state = Paused;
-    QMetaObject::invokeMethod(m_agent, "debuggerPaused", Qt::QueuedConnection,
-                              Q_ARG(QV4::Debugging::Debugger*, this),
-                              Q_ARG(QV4::Debugging::PauseReason, reason));
+    emit debuggerPaused(this, reason);
 
     while (true) {
         m_runningCondition.wait(&m_lock);
@@ -887,117 +858,6 @@ void Debugger::runInEngine_havingLock(Debugger::Job *job)
     m_runningJob = 0;
 }
 
-void DebuggerAgent::addDebugger(Debugger *debugger)
-{
-    Q_ASSERT(!m_debuggers.contains(debugger));
-    m_debuggers << debugger;
-    debugger->attachToAgent(this);
-
-    debugger->setBreakOnThrow(m_breakOnThrow);
-
-    foreach (const BreakPoint &breakPoint, m_breakPoints.values())
-        if (breakPoint.enabled)
-            debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition);
-}
-
-void DebuggerAgent::removeDebugger(Debugger *debugger)
-{
-    m_debuggers.removeAll(debugger);
-    debugger->detachFromAgent();
-}
-
-void DebuggerAgent::pause(Debugger *debugger) const
-{
-    debugger->pause();
-}
-
-void DebuggerAgent::pauseAll() const
-{
-    foreach (Debugger *debugger, m_debuggers)
-        pause(debugger);
-}
-
-void DebuggerAgent::resumeAll() const
-{
-    foreach (Debugger *debugger, m_debuggers)
-        if (debugger->state() == Debugger::Paused)
-            debugger->resume(Debugger::FullThrottle);
-}
-
-int DebuggerAgent::addBreakPoint(const QString &fileName, int lineNumber, bool enabled, const QString &condition)
-{
-    if (enabled)
-        foreach (Debugger *debugger, m_debuggers)
-            debugger->addBreakPoint(fileName, lineNumber, condition);
-
-    int id = m_breakPoints.size();
-    m_breakPoints.insert(id, BreakPoint(fileName, lineNumber, enabled, condition));
-    return id;
-}
-
-void DebuggerAgent::removeBreakPoint(int id)
-{
-    BreakPoint breakPoint = m_breakPoints.value(id);
-    if (!breakPoint.isValid())
-        return;
-
-    m_breakPoints.remove(id);
-
-    if (breakPoint.enabled)
-        foreach (Debugger *debugger, m_debuggers)
-            debugger->removeBreakPoint(breakPoint.fileName, breakPoint.lineNr);
-}
-
-void DebuggerAgent::removeAllBreakPoints()
-{
-    QList<int> ids = m_breakPoints.keys();
-    foreach (int id, ids)
-        removeBreakPoint(id);
-}
-
-void DebuggerAgent::enableBreakPoint(int id, bool onoff)
-{
-    BreakPoint &breakPoint = m_breakPoints[id];
-    if (!breakPoint.isValid() || breakPoint.enabled == onoff)
-        return;
-    breakPoint.enabled = onoff;
-
-    foreach (Debugger *debugger, m_debuggers) {
-        if (onoff)
-            debugger->addBreakPoint(breakPoint.fileName, breakPoint.lineNr, breakPoint.condition);
-        else
-            debugger->removeBreakPoint(breakPoint.fileName, breakPoint.lineNr);
-    }
-}
-
-QList<int> DebuggerAgent::breakPointIds(const QString &fileName, int lineNumber) const
-{
-    QList<int> ids;
-
-    for (QHash<int, BreakPoint>::const_iterator i = m_breakPoints.begin(), ei = m_breakPoints.end(); i != ei; ++i)
-        if (i->lineNr == lineNumber && fileName.endsWith(i->fileName))
-            ids.push_back(i.key());
-
-    return ids;
-}
-
-void DebuggerAgent::setBreakOnThrow(bool onoff)
-{
-    if (onoff != m_breakOnThrow) {
-        m_breakOnThrow = onoff;
-        foreach (Debugger *debugger, m_debuggers)
-            debugger->setBreakOnThrow(onoff);
-    }
-}
-
-DebuggerAgent::~DebuggerAgent()
-{
-    foreach (Debugger *debugger, m_debuggers)
-        debugger->detachFromAgent();
-
-    Q_ASSERT(m_debuggers.isEmpty());
-}
-
 Debugger::Job::~Job()
 {
 }
index 424459a7d924a9972b2e35a77e1be5ef7887af04..b85611f457a30318848fe7ee150ee0974f775d96 100644 (file)
@@ -61,8 +61,6 @@ enum PauseReason {
     Step
 };
 
-class DebuggerAgent;
-
 struct DebuggerBreakPoint {
     DebuggerBreakPoint(const QString &fileName, int line)
         : fileName(fileName), lineNumber(line)
@@ -137,8 +135,9 @@ private:
     DataCollector::Refs *m_previousRefs;
 };
 
-class Q_QML_EXPORT Debugger
+class Q_QML_EXPORT Debugger : public QObject
 {
+    Q_OBJECT
 public:
     class Job
     {
@@ -162,15 +161,10 @@ public:
     };
 
     Debugger(ExecutionEngine *engine);
-    ~Debugger();
 
     ExecutionEngine *engine() const
     { return m_engine; }
 
-    void attachToAgent(DebuggerAgent *agent);
-    void detachFromAgent();
-    DebuggerAgent *agent() const { return m_agent; }
-
     void gatherSources(int requestSequenceNr);
     void pause();
     void resume(Speed speed);
@@ -214,6 +208,10 @@ public: // execution hooks
     void leavingFunction(const ReturnedValue &retVal);
     void aboutToThrow();
 
+signals:
+    void sourcesCollected(QV4::Debugging::Debugger *self, const QStringList &sources, int seq);
+    void debuggerPaused(QV4::Debugging::Debugger *self, QV4::Debugging::PauseReason reason);
+
 private:
     Function *getFunction() const;
 
@@ -228,7 +226,6 @@ private:
 private:
     QV4::ExecutionEngine *m_engine;
     QV4::PersistentValue m_currentContext;
-    DebuggerAgent *m_agent;
     QMutex m_lock;
     QWaitCondition m_runningCondition;
     State m_state;
@@ -245,54 +242,6 @@ private:
     QWaitCondition m_jobIsRunning;
 };
 
-class Q_QML_EXPORT DebuggerAgent : public QObject
-{
-    Q_OBJECT
-public:
-    DebuggerAgent(): m_breakOnThrow(false) {}
-    ~DebuggerAgent();
-
-    void addDebugger(Debugger *debugger);
-    void removeDebugger(Debugger *debugger);
-
-    void pause(Debugger *debugger) const;
-    void pauseAll() const;
-    void resumeAll() const;
-    int addBreakPoint(const QString &fileName, int lineNumber, bool enabled = true, const QString &condition = QString());
-    void removeBreakPoint(int id);
-    void removeAllBreakPoints();
-    void enableBreakPoint(int id, bool onoff);
-    QList<int> breakPointIds(const QString &fileName, int lineNumber) const;
-
-    bool breakOnThrow() const { return m_breakOnThrow; }
-    void setBreakOnThrow(bool onoff);
-
-    Q_INVOKABLE virtual void debuggerPaused(QV4::Debugging::Debugger *debugger,
-                                            QV4::Debugging::PauseReason reason) = 0;
-    Q_INVOKABLE virtual void sourcesCollected(QV4::Debugging::Debugger *debugger,
-                                              QStringList sources, int requestSequenceNr) = 0;
-
-protected:
-    QList<Debugger *> m_debuggers;
-
-    struct BreakPoint {
-        QString fileName;
-        int lineNr;
-        bool enabled;
-        QString condition;
-
-        BreakPoint(): lineNr(-1), enabled(false) {}
-        BreakPoint(const QString &fileName, int lineNr, bool enabled, const QString &condition)
-            : fileName(fileName), lineNr(lineNr), enabled(enabled), condition(condition)
-        {}
-
-        bool isValid() const { return lineNr >= 0 && !fileName.isEmpty(); }
-    };
-
-    QHash<int, BreakPoint> m_breakPoints;
-    bool m_breakOnThrow;
-};
-
 } // namespace Debugging
 } // namespace QV4
 
index 78aa1134509029b7d010b162c12e9b9a057ed039..ac780d272c0ff402bf13c8c22f17206a2a45507f 100644 (file)
@@ -93,7 +93,7 @@ signals:
     void evaluateFinished();
 };
 
-class TestAgent : public QV4::Debugging::DebuggerAgent
+class TestAgent : public QObject
 {
     Q_OBJECT
 public:
@@ -151,12 +151,14 @@ public:
         , m_captureContextInfo(false)
         , m_thrownValue(-1)
         , collector(engine)
+        , m_debugger(0)
     {
     }
 
-    virtual void debuggerPaused(Debugger *debugger, PauseReason reason)
+public slots:
+    void debuggerPaused(QV4::Debugging::Debugger *debugger, QV4::Debugging::PauseReason reason)
     {
-        Q_ASSERT(m_debuggers.count() == 1 && m_debuggers.first() == debugger);
+        Q_ASSERT(debugger == m_debugger);
         Q_ASSERT(debugger->engine() == collector.engine());
         m_wasPaused = true;
         m_pauseReason = reason;
@@ -190,15 +192,7 @@ public:
         debugger->resume(Debugger::FullThrottle);
     }
 
-    virtual void sourcesCollected(Debugger *debugger, QStringList sources, int requestSequenceNr)
-    {
-        Q_UNUSED(debugger);
-        Q_UNUSED(sources);
-        Q_UNUSED(requestSequenceNr);
-    }
-
-    int debuggerCount() const { return m_debuggers.count(); }
-
+public:
     struct TestBreakPoint
     {
         TestBreakPoint() : lineNumber(-1) {}
@@ -221,6 +215,16 @@ public:
         }
     }
 
+    void addDebugger(QV4::Debugging::Debugger *debugger)
+    {
+        Q_ASSERT(!m_debugger);
+        m_debugger = debugger;
+        connect(m_debugger,
+                SIGNAL(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason)),
+                this,
+                SLOT(debuggerPaused(QV4::Debugging::Debugger*,QV4::Debugging::PauseReason)));
+    }
+
     bool m_wasPaused;
     PauseReason m_pauseReason;
     bool m_captureContextInfo;
@@ -238,6 +242,7 @@ public:
     };
     QVector<ExpressionRequest> m_expressionRequests;
     QVector<Refs> m_expressionResults;
+    QV4::Debugging::Debugger *m_debugger;
 
     // Utility methods:
     void dumpStackTrace() const
@@ -315,7 +320,6 @@ void tst_qv4debugger::cleanup()
     delete m_javaScriptThread;
     m_engine = 0;
     m_v4 = 0;
-    QCOMPARE(m_debuggerAgent->debuggerCount(), 0);
     delete m_debuggerAgent;
     m_debuggerAgent = 0;
 }
@@ -326,7 +330,7 @@ void tst_qv4debugger::breakAnywhere()
             "var i = 42;\n"
             "var j = i + 1\n"
             "var k = i\n";
-    m_debuggerAgent->pauseAll();
+    m_v4->debugger->pause();
     evaluateJavaScript(script, "testFile");
     QVERIFY(m_debuggerAgent->m_wasPaused);
 }
@@ -337,7 +341,7 @@ void tst_qv4debugger::pendingBreakpoint()
             "var i = 42;\n"
             "var j = i + 1\n"
             "var k = i\n";
-    m_debuggerAgent->addBreakPoint("testfile", 2);
+    m_v4->debugger->addBreakPoint("testfile", 2);
     evaluateJavaScript(script, "testfile");
     QVERIFY(m_debuggerAgent->m_wasPaused);
     QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 1);
@@ -353,7 +357,7 @@ void tst_qv4debugger::liveBreakPoint()
             "var j = i + 1\n"
             "var k = i\n";
     m_debuggerAgent->m_breakPointsToAddWhenPaused << TestAgent::TestBreakPoint("liveBreakPoint", 3);
-    m_debuggerAgent->pauseAll();
+    m_v4->debugger->pause();
     evaluateJavaScript(script, "liveBreakPoint");
     QVERIFY(m_debuggerAgent->m_wasPaused);
     QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 2);
@@ -368,8 +372,8 @@ void tst_qv4debugger::removePendingBreakPoint()
             "var i = 42;\n"
             "var j = i + 1\n"
             "var k = i\n";
-    int id = m_debuggerAgent->addBreakPoint("removePendingBreakPoint", 2);
-    m_debuggerAgent->removeBreakPoint(id);
+    m_v4->debugger->addBreakPoint("removePendingBreakPoint", 2);
+    m_v4->debugger->removeBreakPoint("removePendingBreakPoint", 2);
     evaluateJavaScript(script, "removePendingBreakPoint");
     QVERIFY(!m_debuggerAgent->m_wasPaused);
 }
@@ -380,7 +384,7 @@ void tst_qv4debugger::addBreakPointWhilePaused()
             "var i = 42;\n"
             "var j = i + 1\n"
             "var k = i\n";
-    m_debuggerAgent->addBreakPoint("addBreakPointWhilePaused", 1);
+    m_v4->debugger->addBreakPoint("addBreakPointWhilePaused", 1);
     m_debuggerAgent->m_breakPointsToAddWhenPaused << TestAgent::TestBreakPoint("addBreakPointWhilePaused", 2);
     evaluateJavaScript(script, "addBreakPointWhilePaused");
     QVERIFY(m_debuggerAgent->m_wasPaused);
@@ -410,7 +414,7 @@ void tst_qv4debugger::removeBreakPointForNextInstruction()
     QMetaObject::invokeMethod(m_engine, "injectFunction", Qt::BlockingQueuedConnection,
                               Q_ARG(QString, "someCall"), Q_ARG(InjectedFunction, someCall));
 
-    m_debuggerAgent->addBreakPoint("removeBreakPointForNextInstruction", 2);
+    m_v4->debugger->addBreakPoint("removeBreakPointForNextInstruction", 2);
 
     evaluateJavaScript(script, "removeBreakPointForNextInstruction");
     QVERIFY(!m_debuggerAgent->m_wasPaused);
@@ -427,7 +431,7 @@ void tst_qv4debugger::conditionalBreakPoint()
             "}\n"
             "test()\n";
 
-    m_debuggerAgent->addBreakPoint("conditionalBreakPoint", 3, /*enabled*/true, QStringLiteral("i > 10"));
+    m_v4->debugger->addBreakPoint("conditionalBreakPoint", 3, QStringLiteral("i > 10"));
     evaluateJavaScript(script, "conditionalBreakPoint");
     QVERIFY(m_debuggerAgent->m_wasPaused);
     QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 4);
@@ -465,7 +469,7 @@ void tst_qv4debugger::conditionalBreakPointInQml()
                       "    }\n"
                       "}\n", QUrl("test.qml"));
 
-    debuggerAgent->addBreakPoint("test.qml", 7, /*enabled*/true, "root.foo == 42");
+    v4->debugger->addBreakPoint("test.qml", 7, "root.foo == 42");
 
     QScopedPointer<QObject> obj(component.create());
     QCOMPARE(obj->property("success").toBool(), true);
@@ -487,7 +491,7 @@ void tst_qv4debugger::readArguments()
             "}\n"
             "var four;\n"
             "f(1, 'two', null, four);\n";
-    m_debuggerAgent->addBreakPoint("readArguments", 2);
+    m_v4->debugger->addBreakPoint("readArguments", 2);
     evaluateJavaScript(script, "readArguments");
     QVERIFY(m_debuggerAgent->m_wasPaused);
     QVERIFY(m_debuggerAgent->m_capturedArguments.size() > 1);
@@ -511,7 +515,7 @@ void tst_qv4debugger::readLocals()
             "  return c === d\n"
             "}\n"
             "f(1, 2, 3);\n";
-    m_debuggerAgent->addBreakPoint("readLocals", 3);
+    m_v4->debugger->addBreakPoint("readLocals", 3);
     evaluateJavaScript(script, "readLocals");
     QVERIFY(m_debuggerAgent->m_wasPaused);
     QVERIFY(m_debuggerAgent->m_capturedLocals.size() > 1);
@@ -533,7 +537,7 @@ void tst_qv4debugger::readObject()
             "  return b\n"
             "}\n"
             "f({head: 1, tail: { head: 'asdf', tail: null }});\n";
-    m_debuggerAgent->addBreakPoint("readObject", 3);
+    m_v4->debugger->addBreakPoint("readObject", 3);
     evaluateJavaScript(script, "readObject");
     QVERIFY(m_debuggerAgent->m_wasPaused);
     QVERIFY(m_debuggerAgent->m_capturedLocals.size() > 1);
@@ -591,7 +595,7 @@ void tst_qv4debugger::readContextInAllFrames()
             "    return 1;\n" // breakpoint
             "}\n"
             "fact(12);\n";
-    m_debuggerAgent->addBreakPoint("readFormalsInAllFrames", 7);
+    m_v4->debugger->addBreakPoint("readFormalsInAllFrames", 7);
     evaluateJavaScript(script, "readFormalsInAllFrames");
     QVERIFY(m_debuggerAgent->m_wasPaused);
     QCOMPARE(m_debuggerAgent->m_stackTrace.size(), 13);
@@ -626,7 +630,7 @@ void tst_qv4debugger::pauseOnThrow()
             "  throw n\n"
             "}\n"
             "die('hard');\n";
-    m_debuggerAgent->setBreakOnThrow(true);
+    m_v4->debugger->setBreakOnThrow(true);
     evaluateJavaScript(script, "pauseOnThrow");
     QVERIFY(m_debuggerAgent->m_wasPaused);
     QCOMPARE(m_debuggerAgent->m_pauseReason, Throwing);
@@ -647,7 +651,7 @@ void tst_qv4debugger::breakInCatch()
             "    console.log(e, 'me');\n"
             "}\n";
 
-    m_debuggerAgent->addBreakPoint("breakInCatch", 4);
+    m_v4->debugger->addBreakPoint("breakInCatch", 4);
     evaluateJavaScript(script, "breakInCatch");
     QVERIFY(m_debuggerAgent->m_wasPaused);
     QCOMPARE(m_debuggerAgent->m_pauseReason, BreakPoint);
@@ -664,7 +668,7 @@ void tst_qv4debugger::breakInWith()
             "    console.log('give the answer');\n"
             "}\n";
 
-    m_debuggerAgent->addBreakPoint("breakInWith", 2);
+    m_v4->debugger->addBreakPoint("breakInWith", 2);
     evaluateJavaScript(script, "breakInWith");
     QVERIFY(m_debuggerAgent->m_wasPaused);
     QCOMPARE(m_debuggerAgent->m_pauseReason, BreakPoint);
@@ -692,7 +696,7 @@ void tst_qv4debugger::evaluateExpression()
     request.frameNr = 1;
     m_debuggerAgent->m_expressionRequests << request;
 
-    m_debuggerAgent->addBreakPoint("evaluateExpression", 3);
+    m_v4->debugger->addBreakPoint("evaluateExpression", 3);
 
     evaluateJavaScript(script, "evaluateExpression");