diff options
-rw-r--r-- | src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro | 6 | ||||
-rw-r--r-- | src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp | 251 | ||||
-rw-r--r-- | src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h | 97 | ||||
-rw-r--r-- | src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp | 84 | ||||
-rw-r--r-- | src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h | 19 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4debugging.cpp | 144 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4debugging_p.h | 63 | ||||
-rw-r--r-- | tests/auto/qml/qv4debugger/tst_qv4debugger.cpp | 64 |
8 files changed, 395 insertions, 333 deletions
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro index 6c29411981..dd0c9a174f 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro +++ b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro @@ -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 index 0000000000..15421f4f5d --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.cpp @@ -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 index 0000000000..6126eea772 --- /dev/null +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debuggeragent.h @@ -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 diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index 62436f18c9..21bfe97808 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -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()); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h index 23bffa368a..6e5113600d 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h @@ -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 diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp index c48bb03844..6f3e48352e 100644 --- a/src/qml/jsruntime/qv4debugging.cpp +++ b/src/qml/jsruntime/qv4debugging.cpp @@ -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() { } diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h index 424459a7d9..b85611f457 100644 --- a/src/qml/jsruntime/qv4debugging_p.h +++ b/src/qml/jsruntime/qv4debugging_p.h @@ -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 diff --git a/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp index 78aa113450..ac780d272c 100644 --- a/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp @@ -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"); |