From 8f4d9ff4bd4c1c5bf6a010324dd295586308b496 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Fri, 12 Jun 2009 18:00:19 +0200 Subject: tidy things up --- all.pro | 2 + debuggee/debuggee.pro | 9 - debuggee/example.qs | 17 - debuggee/foo.qs | 5 - debuggee/main.cpp | 98 ------ debuggee/qscriptdebuggerconnector.cpp | 395 ---------------------- debuggee/qscriptdebuggerconnector.h | 38 --- examples/debuggee/debuggee.pro | 7 + examples/debuggee/example.qs | 17 + examples/debuggee/foo.qs | 5 + examples/debuggee/main.cpp | 98 ++++++ examples/debugger/debugger.pro | 7 + examples/debugger/main.cpp | 47 +++ examples/examples.pro | 3 + main.cpp | 47 --- qscriptdebuggermetatypes.cpp | 65 ---- qscriptremotetargetdebugger.cpp | 596 ---------------------------------- qscriptremotetargetdebugger.h | 96 ------ remotescriptdebugger.pro | 9 - src/debuggerconnector.pri | 4 + src/qscriptdebuggerconnector.cpp | 395 ++++++++++++++++++++++ src/qscriptdebuggerconnector.h | 38 +++ src/qscriptdebuggermetatypes.cpp | 65 ++++ src/qscriptremotetargetdebugger.cpp | 596 ++++++++++++++++++++++++++++++++++ src/qscriptremotetargetdebugger.h | 96 ++++++ src/remotetargetdebugger.pri | 4 + 26 files changed, 1384 insertions(+), 1375 deletions(-) create mode 100644 all.pro delete mode 100644 debuggee/debuggee.pro delete mode 100644 debuggee/example.qs delete mode 100644 debuggee/foo.qs delete mode 100644 debuggee/main.cpp delete mode 100644 debuggee/qscriptdebuggerconnector.cpp delete mode 100644 debuggee/qscriptdebuggerconnector.h create mode 100644 examples/debuggee/debuggee.pro create mode 100644 examples/debuggee/example.qs create mode 100644 examples/debuggee/foo.qs create mode 100644 examples/debuggee/main.cpp create mode 100644 examples/debugger/debugger.pro create mode 100644 examples/debugger/main.cpp create mode 100644 examples/examples.pro delete mode 100644 main.cpp delete mode 100644 qscriptdebuggermetatypes.cpp delete mode 100644 qscriptremotetargetdebugger.cpp delete mode 100644 qscriptremotetargetdebugger.h delete mode 100644 remotescriptdebugger.pro create mode 100644 src/debuggerconnector.pri create mode 100644 src/qscriptdebuggerconnector.cpp create mode 100644 src/qscriptdebuggerconnector.h create mode 100644 src/qscriptdebuggermetatypes.cpp create mode 100644 src/qscriptremotetargetdebugger.cpp create mode 100644 src/qscriptremotetargetdebugger.h create mode 100644 src/remotetargetdebugger.pri diff --git a/all.pro b/all.pro new file mode 100644 index 0000000..61329c1 --- /dev/null +++ b/all.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = examples diff --git a/debuggee/debuggee.pro b/debuggee/debuggee.pro deleted file mode 100644 index de2047e..0000000 --- a/debuggee/debuggee.pro +++ /dev/null @@ -1,9 +0,0 @@ -TEMPLATE = app -TARGET = -DEPENDPATH += . -INCLUDEPATH += . -QT += network script scripttools -# Input -SOURCES += main.cpp -SOURCES += qscriptdebuggerconnector.cpp $$PWD/../qscriptdebuggermetatypes.cpp -HEADERS += qscriptdebuggerconnector.h diff --git a/debuggee/example.qs b/debuggee/example.qs deleted file mode 100644 index 1b545b6..0000000 --- a/debuggee/example.qs +++ /dev/null @@ -1,17 +0,0 @@ -function bar() { - var x = 1; - var y = 2; - return x + y + test(x, y); -} - -function foo(a, b, c) { - var i = a + bar(); - var j = b - bar(); - var k = c * bar(); - return Math.cos(i) + Math.sin(j) - Math.atan(k); -} - -var first = foo(1, 2, 3); -var second = foo(4, 5, 6); -print("first was:", first, ", and second was:", second); - diff --git a/debuggee/foo.qs b/debuggee/foo.qs deleted file mode 100644 index c7d511a..0000000 --- a/debuggee/foo.qs +++ /dev/null @@ -1,5 +0,0 @@ -function test(a, b) { - var c = a * b; - var d = Math.sin(c); - return d + 2; -} diff --git a/debuggee/main.cpp b/debuggee/main.cpp deleted file mode 100644 index 9167f54..0000000 --- a/debuggee/main.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include - -class MyObject : public QObject -{ - Q_OBJECT -public: - MyObject(const QHostAddress &addr, quint16 port, bool connect, QObject *parent = 0); -private slots: - void connected(); - void disconnected(); -private: - QScriptEngine *m_engine; - QScriptDebuggerConnector *m_connector; -}; - -MyObject::MyObject(const QHostAddress &addr, quint16 port, bool connect, QObject *parent) - : QObject(parent) -{ - m_engine = new QScriptEngine(this); - m_connector = new QScriptDebuggerConnector(this); - QObject::connect(m_connector, SIGNAL(connected()), this, SLOT(connected()), Qt::QueuedConnection); - QObject::connect(m_connector, SIGNAL(disconnected()), this, SLOT(disconnected()), Qt::QueuedConnection); - m_connector->setEngine(m_engine); - if (connect) { - qDebug("attempting to connect to debugger at %s:%d", qPrintable(addr.toString()), port); - m_connector->connectToDebugger(addr, port); - } else { - if (m_connector->listen(addr, port)) - qDebug("waiting for debugger to connect at %s:%d", qPrintable(addr.toString()), port); - else { - qWarning("Failed to listen!"); - QCoreApplication::quit(); - } - } -} - -void MyObject::connected() -{ - qDebug("connected!!"); - QStringList fileNames; - QString path = QDir::currentPath(); - fileNames << (path + QDir::separator() + "foo.qs") - << (path + QDir::separator() + "example.qs"); - for (int i = 0; i < fileNames.size(); ++i) { - QString fn = fileNames.at(i); - QFile file(fn); - file.open(QIODevice::ReadOnly); - QString program = file.readAll(); - qDebug("calling evaluate"); - m_engine->evaluate(program, fn); - qDebug("evaluate done"); - } - m_connector->disconnectFromDebugger(); -} - -void MyObject::disconnected() -{ - qDebug("disconnected"); - QCoreApplication::quit(); -} - -int main(int argc, char **argv) -{ - QCoreApplication app(argc, argv); - QHostAddress addr(QHostAddress::LocalHost); - quint16 port = 2000; - bool connect = false; - for (int i = 1; i < argc; ++i) { - QString arg(argv[i]); - arg = arg.trimmed(); - if(arg.startsWith("--")) { - QString opt; - QString val; - int split = arg.indexOf("="); - if(split > 0) { - opt = arg.mid(2).left(split-2); - val = arg.mid(split + 1).trimmed(); - } else { - opt = arg.mid(2); - } - if (opt == QLatin1String("address")) - addr.setAddress(val); - else if (opt == QLatin1String("port")) - port = val.toUShort(); - else if (opt == QLatin1String("connect")) - connect = true; - else if (opt == QLatin1String("help")) { - fprintf(stdout, "Usage: debuggee --address=ADDR --port=NUM [--connect]\n"); - return(0); - } - } - } - MyObject obj(addr, port, connect); - return app.exec(); -} - -#include "main.moc" diff --git a/debuggee/qscriptdebuggerconnector.cpp b/debuggee/qscriptdebuggerconnector.cpp deleted file mode 100644 index 830b0db..0000000 --- a/debuggee/qscriptdebuggerconnector.cpp +++ /dev/null @@ -1,395 +0,0 @@ -#include "qscriptdebuggerconnector.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// #define DEBUGGERCONNECTOR_DEBUG - -void qScriptDebugRegisterMetaTypes(); - -class QScriptRemoteTargetDebuggerBackend : public QObject, - public QScriptDebuggerBackend -{ - Q_OBJECT -public: - enum Error { - NoError, - HostNotFoundError, - ConnectionRefusedError, - HandshakeError, - SocketError - }; - - QScriptRemoteTargetDebuggerBackend(); - ~QScriptRemoteTargetDebuggerBackend(); - - void connectToDebugger(const QHostAddress &address, quint16 port); - void disconnectFromDebugger(); - - bool listen(const QHostAddress &address, quint16 port); - - void resume(); - -Q_SIGNALS: - void connected(); - void disconnected(); - void error(Error error); - -protected: - void event(const QScriptDebuggerEvent &event); - -private Q_SLOTS: - void onSocketStateChanged(QAbstractSocket::SocketState); - void onSocketError(QAbstractSocket::SocketError); - void onReadyRead(); - void onNewConnection(); - -private: - enum State { - UnconnectedState, - HandshakingState, - ConnectedState - }; - -private: - State m_state; - QTcpSocket *m_socket; - int m_blockSize; - QTcpServer *m_server; - QList m_eventLoopPool; - QList m_eventLoopStack; - -private: - Q_DISABLE_COPY(QScriptRemoteTargetDebuggerBackend) -}; - -QScriptRemoteTargetDebuggerBackend::QScriptRemoteTargetDebuggerBackend() - : m_state(UnconnectedState), m_socket(0), m_blockSize(0), m_server(0) -{ - qScriptDebugRegisterMetaTypes(); -} - -QScriptRemoteTargetDebuggerBackend::~QScriptRemoteTargetDebuggerBackend() -{ -} - -void QScriptRemoteTargetDebuggerBackend::connectToDebugger(const QHostAddress &address, quint16 port) -{ - Q_ASSERT(m_state == UnconnectedState); - if (!m_socket) { - m_socket = new QTcpSocket(this); - QObject::connect(m_socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), - this, SLOT(onSocketSateChanged(QAbstractSocket::SocketState))); - QObject::connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(onSocketError(QAbstractSocket::SocketError))); - QObject::connect(m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); - } - m_socket->connectToHost(address, port); -} - -void QScriptRemoteTargetDebuggerBackend::disconnectFromDebugger() -{ - if (!m_socket) - return; - m_socket->disconnectFromHost(); -} - -bool QScriptRemoteTargetDebuggerBackend::listen(const QHostAddress &address, quint16 port) -{ - if (m_socket) - return false; - if (!m_server) { - m_server = new QTcpServer(); - QObject::connect(m_server, SIGNAL(newConnection()), - this, SLOT(onNewConnection())); - } - return m_server->listen(address, port); -} - -void QScriptRemoteTargetDebuggerBackend::onSocketStateChanged(QAbstractSocket::SocketState s) -{ - if (s == QAbstractSocket::ConnectedState) { - m_state = HandshakingState; - } else if (s == QAbstractSocket::UnconnectedState) { - engine()->setAgent(0); - m_state = UnconnectedState; - emit disconnected(); - } -} - -void QScriptRemoteTargetDebuggerBackend::onSocketError(QAbstractSocket::SocketError err) -{ - Q_ASSERT_X(false, Q_FUNC_INFO, "implement me"); -} - -void QScriptRemoteTargetDebuggerBackend::onNewConnection() -{ - m_socket = m_server->nextPendingConnection(); - m_server->close(); - QObject::connect(m_socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), - this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState))); - QObject::connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(onSocketError(QAbstractSocket::SocketError))); - QObject::connect(m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); - // the handshake is initiated by the debugger side, so wait for it - m_state = HandshakingState; -} - -void QScriptRemoteTargetDebuggerBackend::onReadyRead() -{ - switch (m_state) { - case UnconnectedState: - Q_ASSERT(0); - break; - - case HandshakingState: { - QByteArray handshakeData("QtScriptDebug-Handshake"); - if (m_socket->bytesAvailable() == handshakeData.size()) { - QByteArray ba = m_socket->read(handshakeData.size()); - if (ba == handshakeData) { -#ifdef DEBUGGERCONNECTOR_DEBUG - qDebug() << "sending handshake reply (" << handshakeData.size() << "bytes )"; -#endif - m_socket->write(handshakeData); - // handshaking complete - // ### a way to specify if a break should be triggered immediately, - // or only if an uncaught exception is triggered - interruptEvaluation(); - m_state = ConnectedState; - emit connected(); - } else { -// d->error = QScriptDebuggerConnector::HandshakeError; -// d->errorString = QString::fromLatin1("Incorrect handshake data received"); - m_state = UnconnectedState; - emit error(HandshakeError); - m_socket->close(); - } - } - } break; - - case ConnectedState: { -#ifdef DEBUGGERCONNECTOR_DEBUG - qDebug() << "received data. bytesAvailable:" << m_socket->bytesAvailable(); -#endif - QDataStream in(m_socket); - in.setVersion(QDataStream::Qt_4_5); - if (m_blockSize == 0) { - if (m_socket->bytesAvailable() < (int)sizeof(quint32)) - return; - in >> m_blockSize; -#ifdef DEBUGGERCONNECTOR_DEBUG - qDebug() << " blockSize:" << m_blockSize; -#endif - } - if (m_socket->bytesAvailable() < m_blockSize) - return; - -#ifdef DEBUGGERCONNECTOR_DEBUG - qDebug() << "deserializing command"; -#endif - int wasAvailable = m_socket->bytesAvailable(); - qint32 id; - in >> id; - QScriptDebuggerCommand command(QScriptDebuggerCommand::None); - in >> command; - Q_ASSERT(m_socket->bytesAvailable() == wasAvailable - m_blockSize); - -#ifdef DEBUGGERCONNECTOR_DEBUG - qDebug("executing command (id=%d, type=%d)", id, command.type()); -#endif - QScriptDebuggerResponse response = commandExecutor()->execute(this, command); - -#ifdef DEBUGGERCONNECTOR_DEBUG - qDebug() << "serializing response"; -#endif - QByteArray block; - QDataStream out(&block, QIODevice::WriteOnly); - out.setVersion(QDataStream::Qt_4_5); - out << (quint32)0; // reserve 4 bytes for block size - out << (quint8)1; // type = command response - out << id; - out << response; - out.device()->seek(0); - out << (quint32)(block.size() - sizeof(quint32)); -#ifdef DEBUGGERCONNECTOR_DEBUG - qDebug() << "writing response (" << block.size() << "bytes )" << block.toHex(); -#endif - m_socket->write(block); - m_blockSize = 0; - -#ifdef DEBUGGERCONNECTOR_DEBUG - qDebug() << "bytes available is now" << m_socket->bytesAvailable(); -#endif - if (m_socket->bytesAvailable() != 0) - QMetaObject::invokeMethod(this, "onReadyRead", Qt::QueuedConnection); - } break; - - } -} - -/*! - \reimp -*/ -void QScriptRemoteTargetDebuggerBackend::event(const QScriptDebuggerEvent &event) -{ - if (m_state != ConnectedState) - return; - if (m_eventLoopPool.isEmpty()) - m_eventLoopPool.append(new QEventLoop()); - QEventLoop *eventLoop = m_eventLoopPool.takeFirst(); - Q_ASSERT(!eventLoop->isRunning()); - m_eventLoopStack.prepend(eventLoop); - -#ifdef DEBUGGERCONNECTOR_DEBUG - qDebug() << "serializing event of type" << event.type(); -#endif - QByteArray block; - QDataStream out(&block, QIODevice::WriteOnly); - out.setVersion(QDataStream::Qt_4_5); - out << (quint32)0; // reserve 4 bytes for block size - out << (quint8)0; // type = event - out << event; - out.device()->seek(0); - out << (quint32)(block.size() - sizeof(quint32)); - -#ifdef DEBUGGERCONNECTOR_DEBUG - qDebug() << "writing event (" << block.size() << " bytes )"; -#endif - m_socket->write(block); - - // run an event loop until the debugger triggers a resume -#ifdef DEBUGGERCONNECTOR_DEBUG - qDebug("entering event loop"); -#endif - eventLoop->exec(); -#ifdef DEBUGGERCONNECTOR_DEBUG - qDebug("returned from event loop"); -#endif - - if (!m_eventLoopStack.isEmpty()) { - // the event loop was quit directly (i.e. not via resume()) - m_eventLoopStack.takeFirst(); - } - m_eventLoopPool.append(eventLoop); - doPendingEvaluate(/*postEvent=*/false); -} - -/*! - \reimp -*/ -void QScriptRemoteTargetDebuggerBackend::resume() -{ - // quitting the event loops will cause event() to return (see above) - while (!m_eventLoopStack.isEmpty()) { - QEventLoop *eventLoop = m_eventLoopStack.takeFirst(); - if (eventLoop->isRunning()) - eventLoop->quit(); - } -} - -/*! - Constructs a new QScriptDebuggerConnector object with the given \a - parent. -*/ -QScriptDebuggerConnector::QScriptDebuggerConnector(QObject *parent) - : QObject(parent), m_backend(0) -{ -} - -/*! - Destroys this QScriptDebuggerConnector. -*/ -QScriptDebuggerConnector::~QScriptDebuggerConnector() -{ - delete m_backend; -} - -/*! - Sets the \a engine that this connector will manage a connection to. -*/ -void QScriptDebuggerConnector::setEngine(QScriptEngine *engine) -{ - if (m_backend) { - m_backend->detach(); - } else { - m_backend = new QScriptRemoteTargetDebuggerBackend(); - QObject::connect(m_backend, SIGNAL(connected()), - this, SIGNAL(connected())); - QObject::connect(m_backend, SIGNAL(disconnected()), - this, SIGNAL(disconnected())); - } - m_backend->attachTo(engine); -} - -/*! - Returns the \a engine that this connector manages a connection to, - or 0 if no engine has been set. -*/ -QScriptEngine *QScriptDebuggerConnector::engine() const -{ - if (!m_backend) - return 0; - return m_backend->engine(); -} - -/*! - Attempts to make a connection to the given \a address on the given - \a port. - - The connected() signal is emitted when the connection has been - established. - - \sa disconnectFromDebugger(), listen() -*/ -void QScriptDebuggerConnector::connectToDebugger(const QHostAddress &address, quint16 port) -{ - if (!m_backend) { - qWarning("QScriptDebuggerConnector::connectToDebugger(): no engine has been set (call setEngine() first)"); - return; - } - m_backend->connectToDebugger(address, port); -} - -/*! - Attempts to close the connection. - - The disconnected() signal is emitted when the connection has been - closed. - - \sa connectToDebugger() -*/ -void QScriptDebuggerConnector::disconnectFromDebugger() -{ - if (m_backend) - m_backend->disconnectFromDebugger(); -} - -/*! - Listens for an incoming connection on the given \a address and \a - port. - - Returns true on success; otherwise returns false. - - The connected() signal is emitted when a connection has been - established. - - \sa connectToDebugger() -*/ -bool QScriptDebuggerConnector::listen(const QHostAddress &address, quint16 port) -{ - if (!m_backend) { - qWarning("QScriptDebuggerConnector::listen(): no engine has been set (call setEngine() first)"); - return false; - } - return m_backend->listen(address, port); -} - -#include "qscriptdebuggerconnector.moc" diff --git a/debuggee/qscriptdebuggerconnector.h b/debuggee/qscriptdebuggerconnector.h deleted file mode 100644 index 67de9cc..0000000 --- a/debuggee/qscriptdebuggerconnector.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef QSCRIPTDEBUGGERCONNECTOR_H -#define QSCRIPTDEBUGGERCONNECTOR_H - -#include - -#include -//#include - -class QScriptEngine; -class QScriptRemoteTargetDebuggerBackend; - -class QScriptDebuggerConnector : public QObject -{ -Q_OBJECT -public: - QScriptDebuggerConnector(QObject *parent = 0); - ~QScriptDebuggerConnector(); - - void setEngine(QScriptEngine *engine); - QScriptEngine *engine() const; - - void connectToDebugger(const QHostAddress &address, quint16 port); - void disconnectFromDebugger(); - - bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0); - -signals: - void connected(); - void disconnected(); -// void error(Error error); - -private: - QScriptRemoteTargetDebuggerBackend *m_backend; - - Q_DISABLE_COPY(QScriptDebuggerConnector) -}; - -#endif diff --git a/examples/debuggee/debuggee.pro b/examples/debuggee/debuggee.pro new file mode 100644 index 0000000..5e9e631 --- /dev/null +++ b/examples/debuggee/debuggee.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +QT += network script scripttools +include(../../src/debuggerconnector.pri) +SOURCES += main.cpp diff --git a/examples/debuggee/example.qs b/examples/debuggee/example.qs new file mode 100644 index 0000000..1b545b6 --- /dev/null +++ b/examples/debuggee/example.qs @@ -0,0 +1,17 @@ +function bar() { + var x = 1; + var y = 2; + return x + y + test(x, y); +} + +function foo(a, b, c) { + var i = a + bar(); + var j = b - bar(); + var k = c * bar(); + return Math.cos(i) + Math.sin(j) - Math.atan(k); +} + +var first = foo(1, 2, 3); +var second = foo(4, 5, 6); +print("first was:", first, ", and second was:", second); + diff --git a/examples/debuggee/foo.qs b/examples/debuggee/foo.qs new file mode 100644 index 0000000..c7d511a --- /dev/null +++ b/examples/debuggee/foo.qs @@ -0,0 +1,5 @@ +function test(a, b) { + var c = a * b; + var d = Math.sin(c); + return d + 2; +} diff --git a/examples/debuggee/main.cpp b/examples/debuggee/main.cpp new file mode 100644 index 0000000..9167f54 --- /dev/null +++ b/examples/debuggee/main.cpp @@ -0,0 +1,98 @@ +#include +#include + +class MyObject : public QObject +{ + Q_OBJECT +public: + MyObject(const QHostAddress &addr, quint16 port, bool connect, QObject *parent = 0); +private slots: + void connected(); + void disconnected(); +private: + QScriptEngine *m_engine; + QScriptDebuggerConnector *m_connector; +}; + +MyObject::MyObject(const QHostAddress &addr, quint16 port, bool connect, QObject *parent) + : QObject(parent) +{ + m_engine = new QScriptEngine(this); + m_connector = new QScriptDebuggerConnector(this); + QObject::connect(m_connector, SIGNAL(connected()), this, SLOT(connected()), Qt::QueuedConnection); + QObject::connect(m_connector, SIGNAL(disconnected()), this, SLOT(disconnected()), Qt::QueuedConnection); + m_connector->setEngine(m_engine); + if (connect) { + qDebug("attempting to connect to debugger at %s:%d", qPrintable(addr.toString()), port); + m_connector->connectToDebugger(addr, port); + } else { + if (m_connector->listen(addr, port)) + qDebug("waiting for debugger to connect at %s:%d", qPrintable(addr.toString()), port); + else { + qWarning("Failed to listen!"); + QCoreApplication::quit(); + } + } +} + +void MyObject::connected() +{ + qDebug("connected!!"); + QStringList fileNames; + QString path = QDir::currentPath(); + fileNames << (path + QDir::separator() + "foo.qs") + << (path + QDir::separator() + "example.qs"); + for (int i = 0; i < fileNames.size(); ++i) { + QString fn = fileNames.at(i); + QFile file(fn); + file.open(QIODevice::ReadOnly); + QString program = file.readAll(); + qDebug("calling evaluate"); + m_engine->evaluate(program, fn); + qDebug("evaluate done"); + } + m_connector->disconnectFromDebugger(); +} + +void MyObject::disconnected() +{ + qDebug("disconnected"); + QCoreApplication::quit(); +} + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + QHostAddress addr(QHostAddress::LocalHost); + quint16 port = 2000; + bool connect = false; + for (int i = 1; i < argc; ++i) { + QString arg(argv[i]); + arg = arg.trimmed(); + if(arg.startsWith("--")) { + QString opt; + QString val; + int split = arg.indexOf("="); + if(split > 0) { + opt = arg.mid(2).left(split-2); + val = arg.mid(split + 1).trimmed(); + } else { + opt = arg.mid(2); + } + if (opt == QLatin1String("address")) + addr.setAddress(val); + else if (opt == QLatin1String("port")) + port = val.toUShort(); + else if (opt == QLatin1String("connect")) + connect = true; + else if (opt == QLatin1String("help")) { + fprintf(stdout, "Usage: debuggee --address=ADDR --port=NUM [--connect]\n"); + return(0); + } + } + } + MyObject obj(addr, port, connect); + return app.exec(); +} + +#include "main.moc" diff --git a/examples/debugger/debugger.pro b/examples/debugger/debugger.pro new file mode 100644 index 0000000..bbb289b --- /dev/null +++ b/examples/debugger/debugger.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +QT += script scripttools network +include(../../src/remotetargetdebugger.pri) +SOURCES += main.cpp diff --git a/examples/debugger/main.cpp b/examples/debugger/main.cpp new file mode 100644 index 0000000..3d21995 --- /dev/null +++ b/examples/debugger/main.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include "qscriptremotetargetdebugger.h" + +void qScriptDebugRegisterMetaTypes(); + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QHostAddress addr(QHostAddress::LocalHost); + quint16 port = 2000; + bool listen = false; + for (int i = 1; i < argc; ++i) { + QString arg(argv[i]); + arg = arg.trimmed(); + if(arg.startsWith("--")) { + QString opt; + QString val; + int split = arg.indexOf("="); + if(split > 0) { + opt = arg.mid(2).left(split-2); + val = arg.mid(split + 1).trimmed(); + } else { + opt = arg.mid(2); + } + if (opt == QLatin1String("address")) + addr.setAddress(val); + else if (opt == QLatin1String("port")) + port = val.toUShort(); + else if (opt == QLatin1String("listen")) + listen = true; + else if (opt == QLatin1String("help")) { + fprintf(stdout, "Usage: remote --address=ADDR --port=NUM [--listen]\n"); + return(-1); + } + } + } + + qScriptDebugRegisterMetaTypes(); + QScriptRemoteTargetDebugger debugger; + qDebug("attaching to %s:%d", qPrintable(addr.toString()), port); + debugger.attachTo(addr, port); + + return app.exec(); +} diff --git a/examples/examples.pro b/examples/examples.pro new file mode 100644 index 0000000..043eba0 --- /dev/null +++ b/examples/examples.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = debugger \ + debuggee diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 3d21995..0000000 --- a/main.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include -#include -#include "qscriptremotetargetdebugger.h" - -void qScriptDebugRegisterMetaTypes(); - -int main(int argc, char **argv) -{ - QApplication app(argc, argv); - - QHostAddress addr(QHostAddress::LocalHost); - quint16 port = 2000; - bool listen = false; - for (int i = 1; i < argc; ++i) { - QString arg(argv[i]); - arg = arg.trimmed(); - if(arg.startsWith("--")) { - QString opt; - QString val; - int split = arg.indexOf("="); - if(split > 0) { - opt = arg.mid(2).left(split-2); - val = arg.mid(split + 1).trimmed(); - } else { - opt = arg.mid(2); - } - if (opt == QLatin1String("address")) - addr.setAddress(val); - else if (opt == QLatin1String("port")) - port = val.toUShort(); - else if (opt == QLatin1String("listen")) - listen = true; - else if (opt == QLatin1String("help")) { - fprintf(stdout, "Usage: remote --address=ADDR --port=NUM [--listen]\n"); - return(-1); - } - } - } - - qScriptDebugRegisterMetaTypes(); - QScriptRemoteTargetDebugger debugger; - qDebug("attaching to %s:%d", qPrintable(addr.toString()), port); - debugger.attachTo(addr, port); - - return app.exec(); -} diff --git a/qscriptdebuggermetatypes.cpp b/qscriptdebuggermetatypes.cpp deleted file mode 100644 index ac2289e..0000000 --- a/qscriptdebuggermetatypes.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include -#include -#include -#include -#include - -Q_DECLARE_METATYPE(QScriptDebuggerCommand) -Q_DECLARE_METATYPE(QScriptDebuggerResponse) -Q_DECLARE_METATYPE(QScriptDebuggerEvent) -Q_DECLARE_METATYPE(QScriptContextInfo) -Q_DECLARE_METATYPE(QScriptContextInfoList) -Q_DECLARE_METATYPE(QScriptDebuggerValue) -Q_DECLARE_METATYPE(QScriptDebuggerValueList) -Q_DECLARE_METATYPE(QScriptValue::PropertyFlags) -Q_DECLARE_METATYPE(QScriptBreakpointData) -Q_DECLARE_METATYPE(QScriptBreakpointMap) -Q_DECLARE_METATYPE(QScriptScriptData) -Q_DECLARE_METATYPE(QScriptScriptMap) -Q_DECLARE_METATYPE(QScriptScriptsDelta) -Q_DECLARE_METATYPE(QScriptDebuggerValueProperty) -Q_DECLARE_METATYPE(QScriptDebuggerValuePropertyList) -Q_DECLARE_METATYPE(QScriptDebuggerObjectSnapshotDelta) -Q_DECLARE_METATYPE(QList) -Q_DECLARE_METATYPE(QList) - -void qScriptDebugRegisterMetaTypes() -{ - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId(); - qMetaTypeId >(); - qMetaTypeId >(); - - qRegisterMetaTypeStreamOperators("QScriptDebuggerCommand"); - qRegisterMetaTypeStreamOperators("QScriptDebuggerResponse"); - qRegisterMetaTypeStreamOperators("QScriptDebuggerEvent"); - qRegisterMetaTypeStreamOperators("QScriptContextInfo"); - qRegisterMetaTypeStreamOperators("QScriptContextInfoList"); - qRegisterMetaTypeStreamOperators("QScriptBreakpointData"); - qRegisterMetaTypeStreamOperators("QScriptBreakpointMap"); - qRegisterMetaTypeStreamOperators("QScriptScriptData"); - qRegisterMetaTypeStreamOperators("QScriptScriptMap"); - qRegisterMetaTypeStreamOperators("QScriptScriptsDelta"); - qRegisterMetaTypeStreamOperators("QScriptDebuggerValue"); - qRegisterMetaTypeStreamOperators("QScriptDebuggerValueList"); - qRegisterMetaTypeStreamOperators("QScriptDebuggerValueProperty"); - qRegisterMetaTypeStreamOperators("QScriptDebuggerValuePropertyList"); - qRegisterMetaTypeStreamOperators("QScriptDebuggerObjectSnapshotDelta"); - qRegisterMetaTypeStreamOperators >("QList"); - qRegisterMetaTypeStreamOperators >("QList"); -} diff --git a/qscriptremotetargetdebugger.cpp b/qscriptremotetargetdebugger.cpp deleted file mode 100644 index 58ccc3a..0000000 --- a/qscriptremotetargetdebugger.cpp +++ /dev/null @@ -1,596 +0,0 @@ -#include "qscriptremotetargetdebugger.h" -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// #define DEBUG_DEBUGGER - -namespace { - -class WidgetClosedNotifier : public QObject -{ - Q_OBJECT -public: - WidgetClosedNotifier(QWidget *w, QObject *parent = 0) - : QObject(parent), widget(w) - { - w->installEventFilter(this); - } - - bool eventFilter(QObject *watched, QEvent *e) - { - if (watched != widget) - return false; - if (e->type() != QEvent::Close) - return false; - emit widgetClosed(); - return true; - } - -Q_SIGNALS: - void widgetClosed(); - -private: - QWidget *widget; -}; - -} // namespace - -class QScriptRemoteTargetDebuggerFrontend - : public QObject, public QScriptDebuggerFrontend -{ - Q_OBJECT -public: - enum Error { - NoError, - HostNotFoundError, - ConnectionRefusedError, - HandshakeError, - SocketError - }; - - enum State { - UnattachedState, - ConnectingState, - HandshakingState, - AttachedState, - DetachingState - }; - - QScriptRemoteTargetDebuggerFrontend(); - ~QScriptRemoteTargetDebuggerFrontend(); - - void attachTo(const QHostAddress &address, quint16 port); - void detach(); - -Q_SIGNALS: - void attached(); - void detached(); - void error(Error error); - -protected: - void processCommand(int id, const QScriptDebuggerCommand &command); - -private Q_SLOTS: - void onSocketStateChanged(QAbstractSocket::SocketState); - void onSocketError(QAbstractSocket::SocketError); - void onReadyRead(); - -private: - State m_state; - QTcpSocket *m_socket; - int m_blockSize; - - Q_DISABLE_COPY(QScriptRemoteTargetDebuggerFrontend) -}; - -QScriptRemoteTargetDebuggerFrontend::QScriptRemoteTargetDebuggerFrontend() - : m_state(UnattachedState), m_socket(0), m_blockSize(0) -{ -} - -QScriptRemoteTargetDebuggerFrontend::~QScriptRemoteTargetDebuggerFrontend() -{ -} - -void QScriptRemoteTargetDebuggerFrontend::attachTo(const QHostAddress &address, quint16 port) -{ - Q_ASSERT(m_state == UnattachedState); - if (!m_socket) { - m_socket = new QTcpSocket(this); - QObject::connect(m_socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), - this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState))); - QObject::connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(onSocketError(QAbstractSocket::SocketError))); - QObject::connect(m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); - } - m_socket->connectToHost(address, port); -} - -void QScriptRemoteTargetDebuggerFrontend::detach() -{ - Q_ASSERT_X(false, Q_FUNC_INFO, "implement me"); -} - -void QScriptRemoteTargetDebuggerFrontend::onSocketStateChanged(QAbstractSocket::SocketState state) -{ - switch (state) { - case QAbstractSocket::UnconnectedState: - m_state = UnattachedState; - break; - case QAbstractSocket::HostLookupState: - case QAbstractSocket::ConnectingState: - m_state = ConnectingState; - break; - case QAbstractSocket::ConnectedState: { - m_state = HandshakingState; - QByteArray handshakeData("QtScriptDebug-Handshake"); -#ifdef DEBUG_DEBUGGER - qDebug("writing handshake data"); -#endif - m_socket->write(handshakeData); - } break; - case QAbstractSocket::BoundState: - break; - case QAbstractSocket::ClosingState: - Q_ASSERT(0); - break; - case QAbstractSocket::ListeningState: - break; - } -} - -void QScriptRemoteTargetDebuggerFrontend::onSocketError(QAbstractSocket::SocketError error) -{ - Q_ASSERT_X(false, Q_FUNC_INFO, qPrintable(m_socket->errorString())); -} - -void QScriptRemoteTargetDebuggerFrontend::onReadyRead() -{ - switch (m_state) { - case UnattachedState: - case ConnectingState: - case DetachingState: - Q_ASSERT(0); - break; - - case HandshakingState: { - QByteArray handshakeData("QtScriptDebug-Handshake"); - if (m_socket->bytesAvailable() >= handshakeData.size()) { - QByteArray ba = m_socket->read(handshakeData.size()); - if (ba == handshakeData) { -#ifdef DEBUG_DEBUGGER - qDebug("handshake ok!"); -#endif - m_state = AttachedState; - QMetaObject::invokeMethod(this, "emitAttachedSignal", Qt::QueuedConnection); - if (m_socket->bytesAvailable() > 0) - QMetaObject::invokeMethod(this, "onReadyRead", Qt::QueuedConnection); - } else { -// d->error = HandshakeError; -// d->errorString = QString::fromLatin1("Incorrect handshake data received"); - m_state = DetachingState; - emit error(HandshakeError); - m_socket->close(); - } - } - } break; - - case AttachedState: { -#ifdef DEBUG_DEBUGGER - qDebug() << "got something! bytes available:" << m_socket->bytesAvailable(); -#endif - QDataStream in(m_socket); - in.setVersion(QDataStream::Qt_4_5); - if (m_blockSize == 0) { - if (m_socket->bytesAvailable() < (int)sizeof(quint32)) - return; - in >> m_blockSize; -#ifdef DEBUG_DEBUGGER - qDebug() << "blockSize:" << m_blockSize; -#endif - } - if (m_socket->bytesAvailable() < m_blockSize) { -#ifdef DEBUG_DEBUGGER - qDebug("waiting for %lld more bytes...", m_blockSize - m_socket->bytesAvailable()); -#endif - return; - } - - int wasAvailable = m_socket->bytesAvailable(); - quint8 type; - in >> type; - if (type == 0) { - // event -#ifdef DEBUG_DEBUGGER - qDebug("deserializing event"); -#endif - QScriptDebuggerEvent event(QScriptDebuggerEvent::None); - in >> event; -#ifdef DEBUG_DEBUGGER - qDebug("notifying event of type %d", event.type()); -#endif - notifyEvent(event); - } else { - // command response -#ifdef DEBUG_DEBUGGER - qDebug("deserializing command response"); -#endif - qint32 id; - in >> id; - QScriptDebuggerResponse response; - in >> response; -#ifdef DEBUG_DEBUGGER - qDebug("notifying command %d finished", id); -#endif - notifyCommandFinished((int)id, response); - } - Q_ASSERT(m_socket->bytesAvailable() == wasAvailable - m_blockSize); - m_blockSize = 0; -#ifdef DEBUG_DEBUGGER - qDebug("bytes available is now %lld", m_socket->bytesAvailable()); -#endif - if (m_socket->bytesAvailable() != 0) - QMetaObject::invokeMethod(this, "onReadyRead", Qt::QueuedConnection); - } break; - } -} - -void QScriptRemoteTargetDebuggerFrontend::processCommand(int id, const QScriptDebuggerCommand &command) -{ - Q_ASSERT(m_state == AttachedState); - QByteArray block; - QDataStream out(&block, QIODevice::WriteOnly); - out.setVersion(QDataStream::Qt_4_5); - out << (quint32)0; // reserve 4 bytes for block size - out << (qint32)id; - out << command; - out.device()->seek(0); - out << (quint32)(block.size() - sizeof(quint32)); -#ifdef DEBUG_DEBUGGER - qDebug("writing command (id=%d, %d bytes)", id, block.size()); -#endif - m_socket->write(block); -} - -QScriptRemoteTargetDebugger::QScriptRemoteTargetDebugger(QObject *parent) - : QObject(parent), m_frontend(0), m_debugger(0), m_autoShow(true), - m_standardWindow(0) -{ -} - -QScriptRemoteTargetDebugger::~QScriptRemoteTargetDebugger() -{ - delete m_frontend; - delete m_debugger; -} - -void QScriptRemoteTargetDebugger::attachTo(const QHostAddress &address, quint16 port) -{ - createDebugger(); - if (!m_frontend) { - m_frontend = new QScriptRemoteTargetDebuggerFrontend(); - QObject::connect(m_frontend, SIGNAL(attached()), this, SIGNAL(attached())); - QObject::connect(m_frontend, SIGNAL(detached()), this, SIGNAL(detached())); - } - m_frontend->attachTo(address, port); - m_debugger->setFrontend(m_frontend); -} - -void QScriptRemoteTargetDebugger::createDebugger() -{ - if (!m_debugger) { - m_debugger = new QScriptDebugger(); - m_debugger->setWidgetFactory(this); - QObject::connect(m_debugger, SIGNAL(started()), - this, SIGNAL(evaluationResumed())); - QObject::connect(m_debugger, SIGNAL(stopped()), - this, SIGNAL(evaluationSuspended())); - if (m_autoShow) { - QObject::connect(this, SIGNAL(evaluationSuspended()), - this, SLOT(showStandardWindow())); - } - } -} - -QMainWindow *QScriptRemoteTargetDebugger::standardWindow() const -{ - if (m_standardWindow) - return m_standardWindow; - if (!QApplication::instance()) - return 0; - QScriptRemoteTargetDebugger *that = const_cast(this); - - QMainWindow *win = new QMainWindow(); - QDockWidget *scriptsDock = new QDockWidget(win); - scriptsDock->setObjectName(QLatin1String("qtscriptdebugger_scriptsDockWidget")); - scriptsDock->setWindowTitle(QObject::tr("Loaded Scripts")); - scriptsDock->setWidget(widget(ScriptsWidget)); - win->addDockWidget(Qt::LeftDockWidgetArea, scriptsDock); - - QDockWidget *breakpointsDock = new QDockWidget(win); - breakpointsDock->setObjectName(QLatin1String("qtscriptdebugger_breakpointsDockWidget")); - breakpointsDock->setWindowTitle(QObject::tr("Breakpoints")); - breakpointsDock->setWidget(widget(BreakpointsWidget)); - win->addDockWidget(Qt::LeftDockWidgetArea, breakpointsDock); - - QDockWidget *stackDock = new QDockWidget(win); - stackDock->setObjectName(QLatin1String("qtscriptdebugger_stackDockWidget")); - stackDock->setWindowTitle(QObject::tr("Stack")); - stackDock->setWidget(widget(StackWidget)); - win->addDockWidget(Qt::RightDockWidgetArea, stackDock); - - QDockWidget *localsDock = new QDockWidget(win); - localsDock->setObjectName(QLatin1String("qtscriptdebugger_localsDockWidget")); - localsDock->setWindowTitle(QObject::tr("Locals")); - localsDock->setWidget(widget(LocalsWidget)); - win->addDockWidget(Qt::RightDockWidgetArea, localsDock); - - QDockWidget *consoleDock = new QDockWidget(win); - consoleDock->setObjectName(QLatin1String("qtscriptdebugger_consoleDockWidget")); - consoleDock->setWindowTitle(QObject::tr("Console")); - consoleDock->setWidget(widget(ConsoleWidget)); - win->addDockWidget(Qt::BottomDockWidgetArea, consoleDock); - - QDockWidget *debugOutputDock = new QDockWidget(win); - debugOutputDock->setObjectName(QLatin1String("qtscriptdebugger_debugOutputDockWidget")); - debugOutputDock->setWindowTitle(QObject::tr("Debug Output")); - debugOutputDock->setWidget(widget(DebugOutputWidget)); - win->addDockWidget(Qt::BottomDockWidgetArea, debugOutputDock); - - QDockWidget *errorLogDock = new QDockWidget(win); - errorLogDock->setObjectName(QLatin1String("qtscriptdebugger_errorLogDockWidget")); - errorLogDock->setWindowTitle(QObject::tr("Error Log")); - errorLogDock->setWidget(widget(ErrorLogWidget)); - win->addDockWidget(Qt::BottomDockWidgetArea, errorLogDock); - - win->tabifyDockWidget(errorLogDock, debugOutputDock); - win->tabifyDockWidget(debugOutputDock, consoleDock); - - win->addToolBar(Qt::TopToolBarArea, that->createStandardToolBar()); - -#ifndef QT_NO_MENUBAR - win->menuBar()->addMenu(that->createStandardMenu(win)); - - QMenu *editMenu = win->menuBar()->addMenu(QObject::tr("Search")); - editMenu->addAction(action(FindInScriptAction)); - editMenu->addAction(action(FindNextInScriptAction)); - editMenu->addAction(action(FindPreviousInScriptAction)); - editMenu->addSeparator(); - editMenu->addAction(action(GoToLineAction)); - - QMenu *viewMenu = win->menuBar()->addMenu(QObject::tr("View")); - viewMenu->addAction(scriptsDock->toggleViewAction()); - viewMenu->addAction(breakpointsDock->toggleViewAction()); - viewMenu->addAction(stackDock->toggleViewAction()); - viewMenu->addAction(localsDock->toggleViewAction()); - viewMenu->addAction(consoleDock->toggleViewAction()); - viewMenu->addAction(debugOutputDock->toggleViewAction()); - viewMenu->addAction(errorLogDock->toggleViewAction()); -#endif - - QWidget *central = new QWidget(); - QVBoxLayout *vbox = new QVBoxLayout(central); - vbox->addWidget(widget(CodeWidget)); - vbox->addWidget(widget(CodeFinderWidget)); - widget(CodeFinderWidget)->hide(); - win->setCentralWidget(central); - - win->setWindowTitle(QObject::tr("Qt Script Debugger")); - - QSettings settings(QSettings::UserScope, QLatin1String("Trolltech")); - QVariant geometry = settings.value(QLatin1String("Qt/scripttools/debugging/mainWindowGeometry")); - if (geometry.isValid()) - win->restoreGeometry(geometry.toByteArray()); - QVariant state = settings.value(QLatin1String("Qt/scripttools/debugging/mainWindowState")); - if (state.isValid()) - win->restoreState(state.toByteArray()); - - WidgetClosedNotifier *closedNotifier = new WidgetClosedNotifier(win, that); - QObject::connect(closedNotifier, SIGNAL(widgetClosed()), - action(ContinueAction), SLOT(trigger())); - - const_cast(this)->m_standardWindow = win; - return win; -} - -void QScriptRemoteTargetDebugger::showStandardWindow() -{ - (void)standardWindow(); // ensure it's created - m_standardWindow->show(); -} - -QWidget *QScriptRemoteTargetDebugger::widget(DebuggerWidget widget) const -{ - const_cast(this)->createDebugger(); - switch (widget) { - case ConsoleWidget: { - QScriptDebuggerConsoleWidgetInterface *w = m_debugger->consoleWidget(); - if (!w) { - w = new QScriptDebuggerConsoleWidget(); - m_debugger->setConsoleWidget(w); - } - return w; - } - case StackWidget: { - QScriptDebuggerStackWidgetInterface *w = m_debugger->stackWidget(); - if (!w) { - w = new QScriptDebuggerStackWidget(); - m_debugger->setStackWidget(w); - } - return w; - } - case ScriptsWidget: { - QScriptDebuggerScriptsWidgetInterface *w = m_debugger->scriptsWidget(); - if (!w) { - w = new QScriptDebuggerScriptsWidget(); - m_debugger->setScriptsWidget(w); - } - return w; - } - case LocalsWidget: { - QScriptDebuggerLocalsWidgetInterface *w = m_debugger->localsWidget(); - if (!w) { - w = new QScriptDebuggerLocalsWidget(); - m_debugger->setLocalsWidget(w); - } - return w; - } - case CodeWidget: { - QScriptDebuggerCodeWidgetInterface *w = m_debugger->codeWidget(); - if (!w) { - w = new QScriptDebuggerCodeWidget(); - m_debugger->setCodeWidget(w); - } - return w; - } - case CodeFinderWidget: { - QScriptDebuggerCodeFinderWidgetInterface *w = m_debugger->codeFinderWidget(); - if (!w) { - w = new QScriptDebuggerCodeFinderWidget(); - m_debugger->setCodeFinderWidget(w); - } - return w; - } - case BreakpointsWidget: { - QScriptBreakpointsWidgetInterface *w = m_debugger->breakpointsWidget(); - if (!w) { - w = new QScriptBreakpointsWidget(); - m_debugger->setBreakpointsWidget(w); - } - return w; - } - case DebugOutputWidget: { - QScriptDebugOutputWidgetInterface *w = m_debugger->debugOutputWidget(); - if (!w) { - w = new QScriptDebugOutputWidget(); - m_debugger->setDebugOutputWidget(w); - } - return w; - } - case ErrorLogWidget: { - QScriptErrorLogWidgetInterface *w = m_debugger->errorLogWidget(); - if (!w) { - w = new QScriptErrorLogWidget(); - m_debugger->setErrorLogWidget(w); - } - return w; - } - } - return 0; -} - -QAction *QScriptRemoteTargetDebugger::action(DebuggerAction action) const -{ - QScriptRemoteTargetDebugger *that = const_cast(this); - that->createDebugger(); - switch (action) { - case InterruptAction: - return m_debugger->interruptAction(that); - case ContinueAction: - return m_debugger->continueAction(that); - case StepIntoAction: - return m_debugger->stepIntoAction(that); - case StepOverAction: - return m_debugger->stepOverAction(that); - case StepOutAction: - return m_debugger->stepOutAction(that); - case RunToCursorAction: - return m_debugger->runToCursorAction(that); - case RunToNewScriptAction: - return m_debugger->runToNewScriptAction(that); - case ToggleBreakpointAction: - return m_debugger->toggleBreakpointAction(that); - case ClearDebugOutputAction: - return m_debugger->clearDebugOutputAction(that); - case ClearErrorLogAction: - return m_debugger->clearErrorLogAction(that); - case ClearConsoleAction: - return m_debugger->clearConsoleAction(that); - case FindInScriptAction: - return m_debugger->findInScriptAction(that); - case FindNextInScriptAction: - return m_debugger->findNextInScriptAction(that); - case FindPreviousInScriptAction: - return m_debugger->findPreviousInScriptAction(that); - case GoToLineAction: - return m_debugger->goToLineAction(that); - } - return 0; -} - -QToolBar *QScriptRemoteTargetDebugger::createStandardToolBar(QWidget *parent) -{ - QToolBar *tb = new QToolBar(parent); - tb->setObjectName(QLatin1String("qtscriptdebugger_standardToolBar")); - tb->addAction(action(ContinueAction)); - tb->addAction(action(InterruptAction)); - tb->addAction(action(StepIntoAction)); - tb->addAction(action(StepOverAction)); - tb->addAction(action(StepOutAction)); - tb->addAction(action(RunToCursorAction)); - tb->addAction(action(RunToNewScriptAction)); - tb->addSeparator(); - tb->addAction(action(FindInScriptAction)); - return tb; -} - -QMenu *QScriptRemoteTargetDebugger::createStandardMenu(QWidget *parent) -{ - QMenu *menu = new QMenu(parent); - menu->setTitle(QObject::tr("Debug")); - menu->addAction(action(ContinueAction)); - menu->addAction(action(InterruptAction)); - menu->addAction(action(StepIntoAction)); - menu->addAction(action(StepOverAction)); - menu->addAction(action(StepOutAction)); - menu->addAction(action(RunToCursorAction)); - menu->addAction(action(RunToNewScriptAction)); - - menu->addSeparator(); - menu->addAction(action(ToggleBreakpointAction)); - - menu->addSeparator(); - menu->addAction(action(ClearDebugOutputAction)); - menu->addAction(action(ClearErrorLogAction)); - menu->addAction(action(ClearConsoleAction)); - - return menu; -} - -QScriptDebugOutputWidgetInterface *QScriptRemoteTargetDebugger::createDebugOutputWidget() -{ - return new QScriptDebugOutputWidget(); -} - -QScriptDebuggerConsoleWidgetInterface *QScriptRemoteTargetDebugger::createConsoleWidget() -{ - return new QScriptDebuggerConsoleWidget(); -} - -QScriptErrorLogWidgetInterface *QScriptRemoteTargetDebugger::createErrorLogWidget() -{ - return new QScriptErrorLogWidget(); -} - -QScriptDebuggerCodeFinderWidgetInterface *QScriptRemoteTargetDebugger::createCodeFinderWidget() -{ - return new QScriptDebuggerCodeFinderWidget(); -} - -#include "qscriptremotetargetdebugger.moc" diff --git a/qscriptremotetargetdebugger.h b/qscriptremotetargetdebugger.h deleted file mode 100644 index 7c2e473..0000000 --- a/qscriptremotetargetdebugger.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef QSCRIPTREMOTETARGETDEBUGGER_H -#define QSCRIPTREMOTETARGETDEBUGGER_H - -#include - -#include - -class QHostAddress; -class QScriptDebugger; -class QScriptRemoteTargetDebuggerFrontend; -class QAction; -class QWidget; -class QMainWindow; -class QMenu; -class QToolBar; - -class QScriptRemoteTargetDebugger - : public QObject, private QScriptDebuggerWidgetFactoryInterface -{ - Q_OBJECT -public: - enum DebuggerWidget { - ConsoleWidget, - StackWidget, - ScriptsWidget, - LocalsWidget, - CodeWidget, - CodeFinderWidget, - BreakpointsWidget, - DebugOutputWidget, - ErrorLogWidget - }; - - enum DebuggerAction { - InterruptAction, - ContinueAction, - StepIntoAction, - StepOverAction, - StepOutAction, - RunToCursorAction, - RunToNewScriptAction, - ToggleBreakpointAction, - ClearDebugOutputAction, - ClearErrorLogAction, - ClearConsoleAction, - FindInScriptAction, - FindNextInScriptAction, - FindPreviousInScriptAction, - GoToLineAction - }; - - QScriptRemoteTargetDebugger(QObject *parent = 0); - ~QScriptRemoteTargetDebugger(); - - void attachTo(const QHostAddress &address, quint16 port); - void detach(); - - bool autoShowStandardWindow() const; - void setAutoShowStandardWindow(bool autoShow); - - QMainWindow *standardWindow() const; - QToolBar *createStandardToolBar(QWidget *parent = 0); - QMenu *createStandardMenu(QWidget *parent = 0); - - QWidget *widget(DebuggerWidget widget) const; - QAction *action(DebuggerAction action) const; - -Q_SIGNALS: - void attached(); - void detached(); -// void error(Error error); - - void evaluationSuspended(); - void evaluationResumed(); - -private Q_SLOTS: - void showStandardWindow(); - -private: - QScriptDebugOutputWidgetInterface *createDebugOutputWidget(); - QScriptDebuggerConsoleWidgetInterface *createConsoleWidget(); - QScriptErrorLogWidgetInterface *createErrorLogWidget(); - QScriptDebuggerCodeFinderWidgetInterface *createCodeFinderWidget(); - - void createDebugger(); - -private: - QScriptRemoteTargetDebuggerFrontend *m_frontend; - QScriptDebugger *m_debugger; - bool m_autoShow; - QMainWindow *m_standardWindow; - - Q_DISABLE_COPY(QScriptRemoteTargetDebugger) -}; - -#endif diff --git a/remotescriptdebugger.pro b/remotescriptdebugger.pro deleted file mode 100644 index 6f4bfb0..0000000 --- a/remotescriptdebugger.pro +++ /dev/null @@ -1,9 +0,0 @@ -TEMPLATE = app -TARGET = -DEPENDPATH += . -INCLUDEPATH += . -QT += script scripttools network -# Input -SOURCES += main.cpp -SOURCES += qscriptremotetargetdebugger.cpp qscriptdebuggermetatypes.cpp -HEADERS += qscriptremotetargetdebugger.h diff --git a/src/debuggerconnector.pri b/src/debuggerconnector.pri new file mode 100644 index 0000000..872be0f --- /dev/null +++ b/src/debuggerconnector.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +SOURCES += $$PWD/qscriptdebuggerconnector.cpp $$PWD/qscriptdebuggermetatypes.cpp +HEADERS += $$PWD/qscriptdebuggerconnector.h diff --git a/src/qscriptdebuggerconnector.cpp b/src/qscriptdebuggerconnector.cpp new file mode 100644 index 0000000..830b0db --- /dev/null +++ b/src/qscriptdebuggerconnector.cpp @@ -0,0 +1,395 @@ +#include "qscriptdebuggerconnector.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// #define DEBUGGERCONNECTOR_DEBUG + +void qScriptDebugRegisterMetaTypes(); + +class QScriptRemoteTargetDebuggerBackend : public QObject, + public QScriptDebuggerBackend +{ + Q_OBJECT +public: + enum Error { + NoError, + HostNotFoundError, + ConnectionRefusedError, + HandshakeError, + SocketError + }; + + QScriptRemoteTargetDebuggerBackend(); + ~QScriptRemoteTargetDebuggerBackend(); + + void connectToDebugger(const QHostAddress &address, quint16 port); + void disconnectFromDebugger(); + + bool listen(const QHostAddress &address, quint16 port); + + void resume(); + +Q_SIGNALS: + void connected(); + void disconnected(); + void error(Error error); + +protected: + void event(const QScriptDebuggerEvent &event); + +private Q_SLOTS: + void onSocketStateChanged(QAbstractSocket::SocketState); + void onSocketError(QAbstractSocket::SocketError); + void onReadyRead(); + void onNewConnection(); + +private: + enum State { + UnconnectedState, + HandshakingState, + ConnectedState + }; + +private: + State m_state; + QTcpSocket *m_socket; + int m_blockSize; + QTcpServer *m_server; + QList m_eventLoopPool; + QList m_eventLoopStack; + +private: + Q_DISABLE_COPY(QScriptRemoteTargetDebuggerBackend) +}; + +QScriptRemoteTargetDebuggerBackend::QScriptRemoteTargetDebuggerBackend() + : m_state(UnconnectedState), m_socket(0), m_blockSize(0), m_server(0) +{ + qScriptDebugRegisterMetaTypes(); +} + +QScriptRemoteTargetDebuggerBackend::~QScriptRemoteTargetDebuggerBackend() +{ +} + +void QScriptRemoteTargetDebuggerBackend::connectToDebugger(const QHostAddress &address, quint16 port) +{ + Q_ASSERT(m_state == UnconnectedState); + if (!m_socket) { + m_socket = new QTcpSocket(this); + QObject::connect(m_socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(onSocketSateChanged(QAbstractSocket::SocketState))); + QObject::connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(onSocketError(QAbstractSocket::SocketError))); + QObject::connect(m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + } + m_socket->connectToHost(address, port); +} + +void QScriptRemoteTargetDebuggerBackend::disconnectFromDebugger() +{ + if (!m_socket) + return; + m_socket->disconnectFromHost(); +} + +bool QScriptRemoteTargetDebuggerBackend::listen(const QHostAddress &address, quint16 port) +{ + if (m_socket) + return false; + if (!m_server) { + m_server = new QTcpServer(); + QObject::connect(m_server, SIGNAL(newConnection()), + this, SLOT(onNewConnection())); + } + return m_server->listen(address, port); +} + +void QScriptRemoteTargetDebuggerBackend::onSocketStateChanged(QAbstractSocket::SocketState s) +{ + if (s == QAbstractSocket::ConnectedState) { + m_state = HandshakingState; + } else if (s == QAbstractSocket::UnconnectedState) { + engine()->setAgent(0); + m_state = UnconnectedState; + emit disconnected(); + } +} + +void QScriptRemoteTargetDebuggerBackend::onSocketError(QAbstractSocket::SocketError err) +{ + Q_ASSERT_X(false, Q_FUNC_INFO, "implement me"); +} + +void QScriptRemoteTargetDebuggerBackend::onNewConnection() +{ + m_socket = m_server->nextPendingConnection(); + m_server->close(); + QObject::connect(m_socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState))); + QObject::connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(onSocketError(QAbstractSocket::SocketError))); + QObject::connect(m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + // the handshake is initiated by the debugger side, so wait for it + m_state = HandshakingState; +} + +void QScriptRemoteTargetDebuggerBackend::onReadyRead() +{ + switch (m_state) { + case UnconnectedState: + Q_ASSERT(0); + break; + + case HandshakingState: { + QByteArray handshakeData("QtScriptDebug-Handshake"); + if (m_socket->bytesAvailable() == handshakeData.size()) { + QByteArray ba = m_socket->read(handshakeData.size()); + if (ba == handshakeData) { +#ifdef DEBUGGERCONNECTOR_DEBUG + qDebug() << "sending handshake reply (" << handshakeData.size() << "bytes )"; +#endif + m_socket->write(handshakeData); + // handshaking complete + // ### a way to specify if a break should be triggered immediately, + // or only if an uncaught exception is triggered + interruptEvaluation(); + m_state = ConnectedState; + emit connected(); + } else { +// d->error = QScriptDebuggerConnector::HandshakeError; +// d->errorString = QString::fromLatin1("Incorrect handshake data received"); + m_state = UnconnectedState; + emit error(HandshakeError); + m_socket->close(); + } + } + } break; + + case ConnectedState: { +#ifdef DEBUGGERCONNECTOR_DEBUG + qDebug() << "received data. bytesAvailable:" << m_socket->bytesAvailable(); +#endif + QDataStream in(m_socket); + in.setVersion(QDataStream::Qt_4_5); + if (m_blockSize == 0) { + if (m_socket->bytesAvailable() < (int)sizeof(quint32)) + return; + in >> m_blockSize; +#ifdef DEBUGGERCONNECTOR_DEBUG + qDebug() << " blockSize:" << m_blockSize; +#endif + } + if (m_socket->bytesAvailable() < m_blockSize) + return; + +#ifdef DEBUGGERCONNECTOR_DEBUG + qDebug() << "deserializing command"; +#endif + int wasAvailable = m_socket->bytesAvailable(); + qint32 id; + in >> id; + QScriptDebuggerCommand command(QScriptDebuggerCommand::None); + in >> command; + Q_ASSERT(m_socket->bytesAvailable() == wasAvailable - m_blockSize); + +#ifdef DEBUGGERCONNECTOR_DEBUG + qDebug("executing command (id=%d, type=%d)", id, command.type()); +#endif + QScriptDebuggerResponse response = commandExecutor()->execute(this, command); + +#ifdef DEBUGGERCONNECTOR_DEBUG + qDebug() << "serializing response"; +#endif + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_5); + out << (quint32)0; // reserve 4 bytes for block size + out << (quint8)1; // type = command response + out << id; + out << response; + out.device()->seek(0); + out << (quint32)(block.size() - sizeof(quint32)); +#ifdef DEBUGGERCONNECTOR_DEBUG + qDebug() << "writing response (" << block.size() << "bytes )" << block.toHex(); +#endif + m_socket->write(block); + m_blockSize = 0; + +#ifdef DEBUGGERCONNECTOR_DEBUG + qDebug() << "bytes available is now" << m_socket->bytesAvailable(); +#endif + if (m_socket->bytesAvailable() != 0) + QMetaObject::invokeMethod(this, "onReadyRead", Qt::QueuedConnection); + } break; + + } +} + +/*! + \reimp +*/ +void QScriptRemoteTargetDebuggerBackend::event(const QScriptDebuggerEvent &event) +{ + if (m_state != ConnectedState) + return; + if (m_eventLoopPool.isEmpty()) + m_eventLoopPool.append(new QEventLoop()); + QEventLoop *eventLoop = m_eventLoopPool.takeFirst(); + Q_ASSERT(!eventLoop->isRunning()); + m_eventLoopStack.prepend(eventLoop); + +#ifdef DEBUGGERCONNECTOR_DEBUG + qDebug() << "serializing event of type" << event.type(); +#endif + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_5); + out << (quint32)0; // reserve 4 bytes for block size + out << (quint8)0; // type = event + out << event; + out.device()->seek(0); + out << (quint32)(block.size() - sizeof(quint32)); + +#ifdef DEBUGGERCONNECTOR_DEBUG + qDebug() << "writing event (" << block.size() << " bytes )"; +#endif + m_socket->write(block); + + // run an event loop until the debugger triggers a resume +#ifdef DEBUGGERCONNECTOR_DEBUG + qDebug("entering event loop"); +#endif + eventLoop->exec(); +#ifdef DEBUGGERCONNECTOR_DEBUG + qDebug("returned from event loop"); +#endif + + if (!m_eventLoopStack.isEmpty()) { + // the event loop was quit directly (i.e. not via resume()) + m_eventLoopStack.takeFirst(); + } + m_eventLoopPool.append(eventLoop); + doPendingEvaluate(/*postEvent=*/false); +} + +/*! + \reimp +*/ +void QScriptRemoteTargetDebuggerBackend::resume() +{ + // quitting the event loops will cause event() to return (see above) + while (!m_eventLoopStack.isEmpty()) { + QEventLoop *eventLoop = m_eventLoopStack.takeFirst(); + if (eventLoop->isRunning()) + eventLoop->quit(); + } +} + +/*! + Constructs a new QScriptDebuggerConnector object with the given \a + parent. +*/ +QScriptDebuggerConnector::QScriptDebuggerConnector(QObject *parent) + : QObject(parent), m_backend(0) +{ +} + +/*! + Destroys this QScriptDebuggerConnector. +*/ +QScriptDebuggerConnector::~QScriptDebuggerConnector() +{ + delete m_backend; +} + +/*! + Sets the \a engine that this connector will manage a connection to. +*/ +void QScriptDebuggerConnector::setEngine(QScriptEngine *engine) +{ + if (m_backend) { + m_backend->detach(); + } else { + m_backend = new QScriptRemoteTargetDebuggerBackend(); + QObject::connect(m_backend, SIGNAL(connected()), + this, SIGNAL(connected())); + QObject::connect(m_backend, SIGNAL(disconnected()), + this, SIGNAL(disconnected())); + } + m_backend->attachTo(engine); +} + +/*! + Returns the \a engine that this connector manages a connection to, + or 0 if no engine has been set. +*/ +QScriptEngine *QScriptDebuggerConnector::engine() const +{ + if (!m_backend) + return 0; + return m_backend->engine(); +} + +/*! + Attempts to make a connection to the given \a address on the given + \a port. + + The connected() signal is emitted when the connection has been + established. + + \sa disconnectFromDebugger(), listen() +*/ +void QScriptDebuggerConnector::connectToDebugger(const QHostAddress &address, quint16 port) +{ + if (!m_backend) { + qWarning("QScriptDebuggerConnector::connectToDebugger(): no engine has been set (call setEngine() first)"); + return; + } + m_backend->connectToDebugger(address, port); +} + +/*! + Attempts to close the connection. + + The disconnected() signal is emitted when the connection has been + closed. + + \sa connectToDebugger() +*/ +void QScriptDebuggerConnector::disconnectFromDebugger() +{ + if (m_backend) + m_backend->disconnectFromDebugger(); +} + +/*! + Listens for an incoming connection on the given \a address and \a + port. + + Returns true on success; otherwise returns false. + + The connected() signal is emitted when a connection has been + established. + + \sa connectToDebugger() +*/ +bool QScriptDebuggerConnector::listen(const QHostAddress &address, quint16 port) +{ + if (!m_backend) { + qWarning("QScriptDebuggerConnector::listen(): no engine has been set (call setEngine() first)"); + return false; + } + return m_backend->listen(address, port); +} + +#include "qscriptdebuggerconnector.moc" diff --git a/src/qscriptdebuggerconnector.h b/src/qscriptdebuggerconnector.h new file mode 100644 index 0000000..67de9cc --- /dev/null +++ b/src/qscriptdebuggerconnector.h @@ -0,0 +1,38 @@ +#ifndef QSCRIPTDEBUGGERCONNECTOR_H +#define QSCRIPTDEBUGGERCONNECTOR_H + +#include + +#include +//#include + +class QScriptEngine; +class QScriptRemoteTargetDebuggerBackend; + +class QScriptDebuggerConnector : public QObject +{ +Q_OBJECT +public: + QScriptDebuggerConnector(QObject *parent = 0); + ~QScriptDebuggerConnector(); + + void setEngine(QScriptEngine *engine); + QScriptEngine *engine() const; + + void connectToDebugger(const QHostAddress &address, quint16 port); + void disconnectFromDebugger(); + + bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0); + +signals: + void connected(); + void disconnected(); +// void error(Error error); + +private: + QScriptRemoteTargetDebuggerBackend *m_backend; + + Q_DISABLE_COPY(QScriptDebuggerConnector) +}; + +#endif diff --git a/src/qscriptdebuggermetatypes.cpp b/src/qscriptdebuggermetatypes.cpp new file mode 100644 index 0000000..ac2289e --- /dev/null +++ b/src/qscriptdebuggermetatypes.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QScriptDebuggerCommand) +Q_DECLARE_METATYPE(QScriptDebuggerResponse) +Q_DECLARE_METATYPE(QScriptDebuggerEvent) +Q_DECLARE_METATYPE(QScriptContextInfo) +Q_DECLARE_METATYPE(QScriptContextInfoList) +Q_DECLARE_METATYPE(QScriptDebuggerValue) +Q_DECLARE_METATYPE(QScriptDebuggerValueList) +Q_DECLARE_METATYPE(QScriptValue::PropertyFlags) +Q_DECLARE_METATYPE(QScriptBreakpointData) +Q_DECLARE_METATYPE(QScriptBreakpointMap) +Q_DECLARE_METATYPE(QScriptScriptData) +Q_DECLARE_METATYPE(QScriptScriptMap) +Q_DECLARE_METATYPE(QScriptScriptsDelta) +Q_DECLARE_METATYPE(QScriptDebuggerValueProperty) +Q_DECLARE_METATYPE(QScriptDebuggerValuePropertyList) +Q_DECLARE_METATYPE(QScriptDebuggerObjectSnapshotDelta) +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QList) + +void qScriptDebugRegisterMetaTypes() +{ + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId(); + qMetaTypeId >(); + qMetaTypeId >(); + + qRegisterMetaTypeStreamOperators("QScriptDebuggerCommand"); + qRegisterMetaTypeStreamOperators("QScriptDebuggerResponse"); + qRegisterMetaTypeStreamOperators("QScriptDebuggerEvent"); + qRegisterMetaTypeStreamOperators("QScriptContextInfo"); + qRegisterMetaTypeStreamOperators("QScriptContextInfoList"); + qRegisterMetaTypeStreamOperators("QScriptBreakpointData"); + qRegisterMetaTypeStreamOperators("QScriptBreakpointMap"); + qRegisterMetaTypeStreamOperators("QScriptScriptData"); + qRegisterMetaTypeStreamOperators("QScriptScriptMap"); + qRegisterMetaTypeStreamOperators("QScriptScriptsDelta"); + qRegisterMetaTypeStreamOperators("QScriptDebuggerValue"); + qRegisterMetaTypeStreamOperators("QScriptDebuggerValueList"); + qRegisterMetaTypeStreamOperators("QScriptDebuggerValueProperty"); + qRegisterMetaTypeStreamOperators("QScriptDebuggerValuePropertyList"); + qRegisterMetaTypeStreamOperators("QScriptDebuggerObjectSnapshotDelta"); + qRegisterMetaTypeStreamOperators >("QList"); + qRegisterMetaTypeStreamOperators >("QList"); +} diff --git a/src/qscriptremotetargetdebugger.cpp b/src/qscriptremotetargetdebugger.cpp new file mode 100644 index 0000000..58ccc3a --- /dev/null +++ b/src/qscriptremotetargetdebugger.cpp @@ -0,0 +1,596 @@ +#include "qscriptremotetargetdebugger.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// #define DEBUG_DEBUGGER + +namespace { + +class WidgetClosedNotifier : public QObject +{ + Q_OBJECT +public: + WidgetClosedNotifier(QWidget *w, QObject *parent = 0) + : QObject(parent), widget(w) + { + w->installEventFilter(this); + } + + bool eventFilter(QObject *watched, QEvent *e) + { + if (watched != widget) + return false; + if (e->type() != QEvent::Close) + return false; + emit widgetClosed(); + return true; + } + +Q_SIGNALS: + void widgetClosed(); + +private: + QWidget *widget; +}; + +} // namespace + +class QScriptRemoteTargetDebuggerFrontend + : public QObject, public QScriptDebuggerFrontend +{ + Q_OBJECT +public: + enum Error { + NoError, + HostNotFoundError, + ConnectionRefusedError, + HandshakeError, + SocketError + }; + + enum State { + UnattachedState, + ConnectingState, + HandshakingState, + AttachedState, + DetachingState + }; + + QScriptRemoteTargetDebuggerFrontend(); + ~QScriptRemoteTargetDebuggerFrontend(); + + void attachTo(const QHostAddress &address, quint16 port); + void detach(); + +Q_SIGNALS: + void attached(); + void detached(); + void error(Error error); + +protected: + void processCommand(int id, const QScriptDebuggerCommand &command); + +private Q_SLOTS: + void onSocketStateChanged(QAbstractSocket::SocketState); + void onSocketError(QAbstractSocket::SocketError); + void onReadyRead(); + +private: + State m_state; + QTcpSocket *m_socket; + int m_blockSize; + + Q_DISABLE_COPY(QScriptRemoteTargetDebuggerFrontend) +}; + +QScriptRemoteTargetDebuggerFrontend::QScriptRemoteTargetDebuggerFrontend() + : m_state(UnattachedState), m_socket(0), m_blockSize(0) +{ +} + +QScriptRemoteTargetDebuggerFrontend::~QScriptRemoteTargetDebuggerFrontend() +{ +} + +void QScriptRemoteTargetDebuggerFrontend::attachTo(const QHostAddress &address, quint16 port) +{ + Q_ASSERT(m_state == UnattachedState); + if (!m_socket) { + m_socket = new QTcpSocket(this); + QObject::connect(m_socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState))); + QObject::connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(onSocketError(QAbstractSocket::SocketError))); + QObject::connect(m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); + } + m_socket->connectToHost(address, port); +} + +void QScriptRemoteTargetDebuggerFrontend::detach() +{ + Q_ASSERT_X(false, Q_FUNC_INFO, "implement me"); +} + +void QScriptRemoteTargetDebuggerFrontend::onSocketStateChanged(QAbstractSocket::SocketState state) +{ + switch (state) { + case QAbstractSocket::UnconnectedState: + m_state = UnattachedState; + break; + case QAbstractSocket::HostLookupState: + case QAbstractSocket::ConnectingState: + m_state = ConnectingState; + break; + case QAbstractSocket::ConnectedState: { + m_state = HandshakingState; + QByteArray handshakeData("QtScriptDebug-Handshake"); +#ifdef DEBUG_DEBUGGER + qDebug("writing handshake data"); +#endif + m_socket->write(handshakeData); + } break; + case QAbstractSocket::BoundState: + break; + case QAbstractSocket::ClosingState: + Q_ASSERT(0); + break; + case QAbstractSocket::ListeningState: + break; + } +} + +void QScriptRemoteTargetDebuggerFrontend::onSocketError(QAbstractSocket::SocketError error) +{ + Q_ASSERT_X(false, Q_FUNC_INFO, qPrintable(m_socket->errorString())); +} + +void QScriptRemoteTargetDebuggerFrontend::onReadyRead() +{ + switch (m_state) { + case UnattachedState: + case ConnectingState: + case DetachingState: + Q_ASSERT(0); + break; + + case HandshakingState: { + QByteArray handshakeData("QtScriptDebug-Handshake"); + if (m_socket->bytesAvailable() >= handshakeData.size()) { + QByteArray ba = m_socket->read(handshakeData.size()); + if (ba == handshakeData) { +#ifdef DEBUG_DEBUGGER + qDebug("handshake ok!"); +#endif + m_state = AttachedState; + QMetaObject::invokeMethod(this, "emitAttachedSignal", Qt::QueuedConnection); + if (m_socket->bytesAvailable() > 0) + QMetaObject::invokeMethod(this, "onReadyRead", Qt::QueuedConnection); + } else { +// d->error = HandshakeError; +// d->errorString = QString::fromLatin1("Incorrect handshake data received"); + m_state = DetachingState; + emit error(HandshakeError); + m_socket->close(); + } + } + } break; + + case AttachedState: { +#ifdef DEBUG_DEBUGGER + qDebug() << "got something! bytes available:" << m_socket->bytesAvailable(); +#endif + QDataStream in(m_socket); + in.setVersion(QDataStream::Qt_4_5); + if (m_blockSize == 0) { + if (m_socket->bytesAvailable() < (int)sizeof(quint32)) + return; + in >> m_blockSize; +#ifdef DEBUG_DEBUGGER + qDebug() << "blockSize:" << m_blockSize; +#endif + } + if (m_socket->bytesAvailable() < m_blockSize) { +#ifdef DEBUG_DEBUGGER + qDebug("waiting for %lld more bytes...", m_blockSize - m_socket->bytesAvailable()); +#endif + return; + } + + int wasAvailable = m_socket->bytesAvailable(); + quint8 type; + in >> type; + if (type == 0) { + // event +#ifdef DEBUG_DEBUGGER + qDebug("deserializing event"); +#endif + QScriptDebuggerEvent event(QScriptDebuggerEvent::None); + in >> event; +#ifdef DEBUG_DEBUGGER + qDebug("notifying event of type %d", event.type()); +#endif + notifyEvent(event); + } else { + // command response +#ifdef DEBUG_DEBUGGER + qDebug("deserializing command response"); +#endif + qint32 id; + in >> id; + QScriptDebuggerResponse response; + in >> response; +#ifdef DEBUG_DEBUGGER + qDebug("notifying command %d finished", id); +#endif + notifyCommandFinished((int)id, response); + } + Q_ASSERT(m_socket->bytesAvailable() == wasAvailable - m_blockSize); + m_blockSize = 0; +#ifdef DEBUG_DEBUGGER + qDebug("bytes available is now %lld", m_socket->bytesAvailable()); +#endif + if (m_socket->bytesAvailable() != 0) + QMetaObject::invokeMethod(this, "onReadyRead", Qt::QueuedConnection); + } break; + } +} + +void QScriptRemoteTargetDebuggerFrontend::processCommand(int id, const QScriptDebuggerCommand &command) +{ + Q_ASSERT(m_state == AttachedState); + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_4_5); + out << (quint32)0; // reserve 4 bytes for block size + out << (qint32)id; + out << command; + out.device()->seek(0); + out << (quint32)(block.size() - sizeof(quint32)); +#ifdef DEBUG_DEBUGGER + qDebug("writing command (id=%d, %d bytes)", id, block.size()); +#endif + m_socket->write(block); +} + +QScriptRemoteTargetDebugger::QScriptRemoteTargetDebugger(QObject *parent) + : QObject(parent), m_frontend(0), m_debugger(0), m_autoShow(true), + m_standardWindow(0) +{ +} + +QScriptRemoteTargetDebugger::~QScriptRemoteTargetDebugger() +{ + delete m_frontend; + delete m_debugger; +} + +void QScriptRemoteTargetDebugger::attachTo(const QHostAddress &address, quint16 port) +{ + createDebugger(); + if (!m_frontend) { + m_frontend = new QScriptRemoteTargetDebuggerFrontend(); + QObject::connect(m_frontend, SIGNAL(attached()), this, SIGNAL(attached())); + QObject::connect(m_frontend, SIGNAL(detached()), this, SIGNAL(detached())); + } + m_frontend->attachTo(address, port); + m_debugger->setFrontend(m_frontend); +} + +void QScriptRemoteTargetDebugger::createDebugger() +{ + if (!m_debugger) { + m_debugger = new QScriptDebugger(); + m_debugger->setWidgetFactory(this); + QObject::connect(m_debugger, SIGNAL(started()), + this, SIGNAL(evaluationResumed())); + QObject::connect(m_debugger, SIGNAL(stopped()), + this, SIGNAL(evaluationSuspended())); + if (m_autoShow) { + QObject::connect(this, SIGNAL(evaluationSuspended()), + this, SLOT(showStandardWindow())); + } + } +} + +QMainWindow *QScriptRemoteTargetDebugger::standardWindow() const +{ + if (m_standardWindow) + return m_standardWindow; + if (!QApplication::instance()) + return 0; + QScriptRemoteTargetDebugger *that = const_cast(this); + + QMainWindow *win = new QMainWindow(); + QDockWidget *scriptsDock = new QDockWidget(win); + scriptsDock->setObjectName(QLatin1String("qtscriptdebugger_scriptsDockWidget")); + scriptsDock->setWindowTitle(QObject::tr("Loaded Scripts")); + scriptsDock->setWidget(widget(ScriptsWidget)); + win->addDockWidget(Qt::LeftDockWidgetArea, scriptsDock); + + QDockWidget *breakpointsDock = new QDockWidget(win); + breakpointsDock->setObjectName(QLatin1String("qtscriptdebugger_breakpointsDockWidget")); + breakpointsDock->setWindowTitle(QObject::tr("Breakpoints")); + breakpointsDock->setWidget(widget(BreakpointsWidget)); + win->addDockWidget(Qt::LeftDockWidgetArea, breakpointsDock); + + QDockWidget *stackDock = new QDockWidget(win); + stackDock->setObjectName(QLatin1String("qtscriptdebugger_stackDockWidget")); + stackDock->setWindowTitle(QObject::tr("Stack")); + stackDock->setWidget(widget(StackWidget)); + win->addDockWidget(Qt::RightDockWidgetArea, stackDock); + + QDockWidget *localsDock = new QDockWidget(win); + localsDock->setObjectName(QLatin1String("qtscriptdebugger_localsDockWidget")); + localsDock->setWindowTitle(QObject::tr("Locals")); + localsDock->setWidget(widget(LocalsWidget)); + win->addDockWidget(Qt::RightDockWidgetArea, localsDock); + + QDockWidget *consoleDock = new QDockWidget(win); + consoleDock->setObjectName(QLatin1String("qtscriptdebugger_consoleDockWidget")); + consoleDock->setWindowTitle(QObject::tr("Console")); + consoleDock->setWidget(widget(ConsoleWidget)); + win->addDockWidget(Qt::BottomDockWidgetArea, consoleDock); + + QDockWidget *debugOutputDock = new QDockWidget(win); + debugOutputDock->setObjectName(QLatin1String("qtscriptdebugger_debugOutputDockWidget")); + debugOutputDock->setWindowTitle(QObject::tr("Debug Output")); + debugOutputDock->setWidget(widget(DebugOutputWidget)); + win->addDockWidget(Qt::BottomDockWidgetArea, debugOutputDock); + + QDockWidget *errorLogDock = new QDockWidget(win); + errorLogDock->setObjectName(QLatin1String("qtscriptdebugger_errorLogDockWidget")); + errorLogDock->setWindowTitle(QObject::tr("Error Log")); + errorLogDock->setWidget(widget(ErrorLogWidget)); + win->addDockWidget(Qt::BottomDockWidgetArea, errorLogDock); + + win->tabifyDockWidget(errorLogDock, debugOutputDock); + win->tabifyDockWidget(debugOutputDock, consoleDock); + + win->addToolBar(Qt::TopToolBarArea, that->createStandardToolBar()); + +#ifndef QT_NO_MENUBAR + win->menuBar()->addMenu(that->createStandardMenu(win)); + + QMenu *editMenu = win->menuBar()->addMenu(QObject::tr("Search")); + editMenu->addAction(action(FindInScriptAction)); + editMenu->addAction(action(FindNextInScriptAction)); + editMenu->addAction(action(FindPreviousInScriptAction)); + editMenu->addSeparator(); + editMenu->addAction(action(GoToLineAction)); + + QMenu *viewMenu = win->menuBar()->addMenu(QObject::tr("View")); + viewMenu->addAction(scriptsDock->toggleViewAction()); + viewMenu->addAction(breakpointsDock->toggleViewAction()); + viewMenu->addAction(stackDock->toggleViewAction()); + viewMenu->addAction(localsDock->toggleViewAction()); + viewMenu->addAction(consoleDock->toggleViewAction()); + viewMenu->addAction(debugOutputDock->toggleViewAction()); + viewMenu->addAction(errorLogDock->toggleViewAction()); +#endif + + QWidget *central = new QWidget(); + QVBoxLayout *vbox = new QVBoxLayout(central); + vbox->addWidget(widget(CodeWidget)); + vbox->addWidget(widget(CodeFinderWidget)); + widget(CodeFinderWidget)->hide(); + win->setCentralWidget(central); + + win->setWindowTitle(QObject::tr("Qt Script Debugger")); + + QSettings settings(QSettings::UserScope, QLatin1String("Trolltech")); + QVariant geometry = settings.value(QLatin1String("Qt/scripttools/debugging/mainWindowGeometry")); + if (geometry.isValid()) + win->restoreGeometry(geometry.toByteArray()); + QVariant state = settings.value(QLatin1String("Qt/scripttools/debugging/mainWindowState")); + if (state.isValid()) + win->restoreState(state.toByteArray()); + + WidgetClosedNotifier *closedNotifier = new WidgetClosedNotifier(win, that); + QObject::connect(closedNotifier, SIGNAL(widgetClosed()), + action(ContinueAction), SLOT(trigger())); + + const_cast(this)->m_standardWindow = win; + return win; +} + +void QScriptRemoteTargetDebugger::showStandardWindow() +{ + (void)standardWindow(); // ensure it's created + m_standardWindow->show(); +} + +QWidget *QScriptRemoteTargetDebugger::widget(DebuggerWidget widget) const +{ + const_cast(this)->createDebugger(); + switch (widget) { + case ConsoleWidget: { + QScriptDebuggerConsoleWidgetInterface *w = m_debugger->consoleWidget(); + if (!w) { + w = new QScriptDebuggerConsoleWidget(); + m_debugger->setConsoleWidget(w); + } + return w; + } + case StackWidget: { + QScriptDebuggerStackWidgetInterface *w = m_debugger->stackWidget(); + if (!w) { + w = new QScriptDebuggerStackWidget(); + m_debugger->setStackWidget(w); + } + return w; + } + case ScriptsWidget: { + QScriptDebuggerScriptsWidgetInterface *w = m_debugger->scriptsWidget(); + if (!w) { + w = new QScriptDebuggerScriptsWidget(); + m_debugger->setScriptsWidget(w); + } + return w; + } + case LocalsWidget: { + QScriptDebuggerLocalsWidgetInterface *w = m_debugger->localsWidget(); + if (!w) { + w = new QScriptDebuggerLocalsWidget(); + m_debugger->setLocalsWidget(w); + } + return w; + } + case CodeWidget: { + QScriptDebuggerCodeWidgetInterface *w = m_debugger->codeWidget(); + if (!w) { + w = new QScriptDebuggerCodeWidget(); + m_debugger->setCodeWidget(w); + } + return w; + } + case CodeFinderWidget: { + QScriptDebuggerCodeFinderWidgetInterface *w = m_debugger->codeFinderWidget(); + if (!w) { + w = new QScriptDebuggerCodeFinderWidget(); + m_debugger->setCodeFinderWidget(w); + } + return w; + } + case BreakpointsWidget: { + QScriptBreakpointsWidgetInterface *w = m_debugger->breakpointsWidget(); + if (!w) { + w = new QScriptBreakpointsWidget(); + m_debugger->setBreakpointsWidget(w); + } + return w; + } + case DebugOutputWidget: { + QScriptDebugOutputWidgetInterface *w = m_debugger->debugOutputWidget(); + if (!w) { + w = new QScriptDebugOutputWidget(); + m_debugger->setDebugOutputWidget(w); + } + return w; + } + case ErrorLogWidget: { + QScriptErrorLogWidgetInterface *w = m_debugger->errorLogWidget(); + if (!w) { + w = new QScriptErrorLogWidget(); + m_debugger->setErrorLogWidget(w); + } + return w; + } + } + return 0; +} + +QAction *QScriptRemoteTargetDebugger::action(DebuggerAction action) const +{ + QScriptRemoteTargetDebugger *that = const_cast(this); + that->createDebugger(); + switch (action) { + case InterruptAction: + return m_debugger->interruptAction(that); + case ContinueAction: + return m_debugger->continueAction(that); + case StepIntoAction: + return m_debugger->stepIntoAction(that); + case StepOverAction: + return m_debugger->stepOverAction(that); + case StepOutAction: + return m_debugger->stepOutAction(that); + case RunToCursorAction: + return m_debugger->runToCursorAction(that); + case RunToNewScriptAction: + return m_debugger->runToNewScriptAction(that); + case ToggleBreakpointAction: + return m_debugger->toggleBreakpointAction(that); + case ClearDebugOutputAction: + return m_debugger->clearDebugOutputAction(that); + case ClearErrorLogAction: + return m_debugger->clearErrorLogAction(that); + case ClearConsoleAction: + return m_debugger->clearConsoleAction(that); + case FindInScriptAction: + return m_debugger->findInScriptAction(that); + case FindNextInScriptAction: + return m_debugger->findNextInScriptAction(that); + case FindPreviousInScriptAction: + return m_debugger->findPreviousInScriptAction(that); + case GoToLineAction: + return m_debugger->goToLineAction(that); + } + return 0; +} + +QToolBar *QScriptRemoteTargetDebugger::createStandardToolBar(QWidget *parent) +{ + QToolBar *tb = new QToolBar(parent); + tb->setObjectName(QLatin1String("qtscriptdebugger_standardToolBar")); + tb->addAction(action(ContinueAction)); + tb->addAction(action(InterruptAction)); + tb->addAction(action(StepIntoAction)); + tb->addAction(action(StepOverAction)); + tb->addAction(action(StepOutAction)); + tb->addAction(action(RunToCursorAction)); + tb->addAction(action(RunToNewScriptAction)); + tb->addSeparator(); + tb->addAction(action(FindInScriptAction)); + return tb; +} + +QMenu *QScriptRemoteTargetDebugger::createStandardMenu(QWidget *parent) +{ + QMenu *menu = new QMenu(parent); + menu->setTitle(QObject::tr("Debug")); + menu->addAction(action(ContinueAction)); + menu->addAction(action(InterruptAction)); + menu->addAction(action(StepIntoAction)); + menu->addAction(action(StepOverAction)); + menu->addAction(action(StepOutAction)); + menu->addAction(action(RunToCursorAction)); + menu->addAction(action(RunToNewScriptAction)); + + menu->addSeparator(); + menu->addAction(action(ToggleBreakpointAction)); + + menu->addSeparator(); + menu->addAction(action(ClearDebugOutputAction)); + menu->addAction(action(ClearErrorLogAction)); + menu->addAction(action(ClearConsoleAction)); + + return menu; +} + +QScriptDebugOutputWidgetInterface *QScriptRemoteTargetDebugger::createDebugOutputWidget() +{ + return new QScriptDebugOutputWidget(); +} + +QScriptDebuggerConsoleWidgetInterface *QScriptRemoteTargetDebugger::createConsoleWidget() +{ + return new QScriptDebuggerConsoleWidget(); +} + +QScriptErrorLogWidgetInterface *QScriptRemoteTargetDebugger::createErrorLogWidget() +{ + return new QScriptErrorLogWidget(); +} + +QScriptDebuggerCodeFinderWidgetInterface *QScriptRemoteTargetDebugger::createCodeFinderWidget() +{ + return new QScriptDebuggerCodeFinderWidget(); +} + +#include "qscriptremotetargetdebugger.moc" diff --git a/src/qscriptremotetargetdebugger.h b/src/qscriptremotetargetdebugger.h new file mode 100644 index 0000000..7c2e473 --- /dev/null +++ b/src/qscriptremotetargetdebugger.h @@ -0,0 +1,96 @@ +#ifndef QSCRIPTREMOTETARGETDEBUGGER_H +#define QSCRIPTREMOTETARGETDEBUGGER_H + +#include + +#include + +class QHostAddress; +class QScriptDebugger; +class QScriptRemoteTargetDebuggerFrontend; +class QAction; +class QWidget; +class QMainWindow; +class QMenu; +class QToolBar; + +class QScriptRemoteTargetDebugger + : public QObject, private QScriptDebuggerWidgetFactoryInterface +{ + Q_OBJECT +public: + enum DebuggerWidget { + ConsoleWidget, + StackWidget, + ScriptsWidget, + LocalsWidget, + CodeWidget, + CodeFinderWidget, + BreakpointsWidget, + DebugOutputWidget, + ErrorLogWidget + }; + + enum DebuggerAction { + InterruptAction, + ContinueAction, + StepIntoAction, + StepOverAction, + StepOutAction, + RunToCursorAction, + RunToNewScriptAction, + ToggleBreakpointAction, + ClearDebugOutputAction, + ClearErrorLogAction, + ClearConsoleAction, + FindInScriptAction, + FindNextInScriptAction, + FindPreviousInScriptAction, + GoToLineAction + }; + + QScriptRemoteTargetDebugger(QObject *parent = 0); + ~QScriptRemoteTargetDebugger(); + + void attachTo(const QHostAddress &address, quint16 port); + void detach(); + + bool autoShowStandardWindow() const; + void setAutoShowStandardWindow(bool autoShow); + + QMainWindow *standardWindow() const; + QToolBar *createStandardToolBar(QWidget *parent = 0); + QMenu *createStandardMenu(QWidget *parent = 0); + + QWidget *widget(DebuggerWidget widget) const; + QAction *action(DebuggerAction action) const; + +Q_SIGNALS: + void attached(); + void detached(); +// void error(Error error); + + void evaluationSuspended(); + void evaluationResumed(); + +private Q_SLOTS: + void showStandardWindow(); + +private: + QScriptDebugOutputWidgetInterface *createDebugOutputWidget(); + QScriptDebuggerConsoleWidgetInterface *createConsoleWidget(); + QScriptErrorLogWidgetInterface *createErrorLogWidget(); + QScriptDebuggerCodeFinderWidgetInterface *createCodeFinderWidget(); + + void createDebugger(); + +private: + QScriptRemoteTargetDebuggerFrontend *m_frontend; + QScriptDebugger *m_debugger; + bool m_autoShow; + QMainWindow *m_standardWindow; + + Q_DISABLE_COPY(QScriptRemoteTargetDebugger) +}; + +#endif diff --git a/src/remotetargetdebugger.pri b/src/remotetargetdebugger.pri new file mode 100644 index 0000000..5aade7e --- /dev/null +++ b/src/remotetargetdebugger.pri @@ -0,0 +1,4 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +SOURCES += $$PWD/qscriptremotetargetdebugger.cpp $$PWD/qscriptdebuggermetatypes.cpp +HEADERS += $$PWD/qscriptremotetargetdebugger.h -- cgit v1.2.3