diff options
author | Kent Hansen <khansen@trolltech.com> | 2009-05-08 11:57:47 +0200 |
---|---|---|
committer | Kent Hansen <khansen@trolltech.com> | 2009-05-08 11:57:47 +0200 |
commit | 7db7c6bd5e47b6d329aba162cb1afd2d9e7b8757 (patch) | |
tree | bf65828bc7fe18b32ffc850830959b74e6463564 |
initialize repo
-rw-r--r-- | debuggee/debuggee.pro | 9 | ||||
-rw-r--r-- | debuggee/example.qs | 17 | ||||
-rw-r--r-- | debuggee/foo.qs | 5 | ||||
-rw-r--r-- | debuggee/main.cpp | 110 | ||||
-rw-r--r-- | debuggee/qscriptdebuggerconnector.cpp | 452 | ||||
-rw-r--r-- | debuggee/qscriptdebuggerconnector.h | 54 | ||||
-rw-r--r-- | main.cpp | 12 | ||||
-rw-r--r-- | qscriptremotetargetdebugger.cpp | 575 | ||||
-rw-r--r-- | qscriptremotetargetdebugger.h | 107 | ||||
-rw-r--r-- | remotescriptdebugger.pro | 9 |
10 files changed, 1350 insertions, 0 deletions
diff --git a/debuggee/debuggee.pro b/debuggee/debuggee.pro new file mode 100644 index 0000000..d4b3eed --- /dev/null +++ b/debuggee/debuggee.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +QT += network script scripttools +# Input +SOURCES += main.cpp +SOURCES += qscriptdebuggerconnector.cpp +HEADERS += qscriptdebuggerconnector.h diff --git a/debuggee/example.qs b/debuggee/example.qs new file mode 100644 index 0000000..1b545b6 --- /dev/null +++ b/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/debuggee/foo.qs b/debuggee/foo.qs new file mode 100644 index 0000000..c7d511a --- /dev/null +++ b/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/debuggee/main.cpp b/debuggee/main.cpp new file mode 100644 index 0000000..d1a19d7 --- /dev/null +++ b/debuggee/main.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2007-$THISYEAR$ $TROLLTECH$. All rights reserved. +** +** This file is part of the Qt Script Debug project on Trolltech Labs. +** +** $TROLLTECH_GPL_LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ +#include <QtScript> +#include <qscriptdebuggerconnector.h> + +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 new file mode 100644 index 0000000..c5fbdb3 --- /dev/null +++ b/debuggee/qscriptdebuggerconnector.cpp @@ -0,0 +1,452 @@ +/**************************************************************************** +** +** Copyright (C) 2007-$THISYEAR$ $TROLLTECH$. All rights reserved. +** +** This file is part of the Qt Script Debug project on Trolltech Labs. +** +** $TROLLTECH_GPL_LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qscriptdebuggerconnector.h" +#include <QtNetwork> +#include <QtScript> +#include <private/qscriptdebuggerbackend_p.h> +#include <private/qscriptdebuggercommand_p.h> +#include <private/qscriptdebuggerevent_p.h> +#include <private/qscriptdebuggerresponse_p.h> +#include <private/qscriptdebuggercommandexecutor_p.h> +#include <private/qscriptbreakpointdata_p.h> +#include <private/qscriptdebuggerobjectsnapshotdelta_p.h> + +#define DEBUGGERCONNECTOR_DEBUG + +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<int>) +Q_DECLARE_METATYPE(QList<qint64>) + +void qScriptDebugRegisterMetaTypes() +{ + qMetaTypeId<QScriptDebuggerCommand>(); + qMetaTypeId<QScriptDebuggerResponse>(); + qMetaTypeId<QScriptDebuggerEvent>(); + qMetaTypeId<QScriptContextInfo>(); + qMetaTypeId<QScriptContextInfoList>(); + qMetaTypeId<QScriptDebuggerValue>(); + qMetaTypeId<QScriptDebuggerValueList>(); + qMetaTypeId<QScriptValue::PropertyFlags>(); + qMetaTypeId<QScriptBreakpointData>(); + qMetaTypeId<QScriptBreakpointMap>(); + qMetaTypeId<QScriptScriptData>(); + qMetaTypeId<QScriptScriptMap>(); + qMetaTypeId<QScriptScriptsDelta>(); + qMetaTypeId<QScriptDebuggerValueProperty>(); + qMetaTypeId<QScriptDebuggerValuePropertyList>(); + qMetaTypeId<QScriptDebuggerObjectSnapshotDelta>(); + qMetaTypeId<QList<int> >(); + qMetaTypeId<QList<qint64> >(); + + qRegisterMetaTypeStreamOperators<QScriptDebuggerCommand>("QScriptDebuggerCommand"); + qRegisterMetaTypeStreamOperators<QScriptDebuggerResponse>("QScriptDebuggerResponse"); + qRegisterMetaTypeStreamOperators<QScriptDebuggerEvent>("QScriptDebuggerEvent"); + qRegisterMetaTypeStreamOperators<QScriptContextInfo>("QScriptContextInfo"); + qRegisterMetaTypeStreamOperators<QScriptContextInfoList>("QScriptContextInfoList"); + qRegisterMetaTypeStreamOperators<QScriptBreakpointData>("QScriptBreakpointData"); + qRegisterMetaTypeStreamOperators<QScriptBreakpointMap>("QScriptBreakpointMap"); + qRegisterMetaTypeStreamOperators<QScriptScriptData>("QScriptScriptData"); + qRegisterMetaTypeStreamOperators<QScriptScriptMap>("QScriptScriptMap"); + qRegisterMetaTypeStreamOperators<QScriptScriptsDelta>("QScriptScriptsDelta"); + qRegisterMetaTypeStreamOperators<QScriptDebuggerValue>("QScriptDebuggerValue"); + qRegisterMetaTypeStreamOperators<QScriptDebuggerValueList>("QScriptDebuggerValueList"); + qRegisterMetaTypeStreamOperators<QScriptDebuggerValueProperty>("QScriptDebuggerValueProperty"); + qRegisterMetaTypeStreamOperators<QScriptDebuggerValuePropertyList>("QScriptDebuggerValuePropertyList"); + qRegisterMetaTypeStreamOperators<QScriptDebuggerObjectSnapshotDelta>("QScriptDebuggerObjectSnapshotDelta"); + qRegisterMetaTypeStreamOperators<QList<int> >("QList<int>"); + qRegisterMetaTypeStreamOperators<QList<qint64> >("QList<qint64>"); +} + +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<QEventLoop*> m_eventLoopPool; + QList<QEventLoop*> 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; + + } +} + +void QScriptRemoteTargetDebuggerBackend::resume() +{ + Q_ASSERT_X(false, Q_FUNC_INFO, "implement me"); +} + +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); +} + +/*! + 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 new file mode 100644 index 0000000..b135ef5 --- /dev/null +++ b/debuggee/qscriptdebuggerconnector.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2007-$THISYEAR$ $TROLLTECH$. All rights reserved. +** +** This file is part of the Qt Script Debug project on Trolltech Labs. +** +** $TROLLTECH_GPL_LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef QSCRIPTDEBUGGERCONNECTOR_H +#define QSCRIPTDEBUGGERCONNECTOR_H + +#include <QtCore/qobject.h> + +#include <QtNetwork/qhostaddress.h> +#include <QtNetwork/qabstractsocket.h> + +class QScriptEngine; +class QScriptRemoteTargetDebuggerBackend; +class QScriptValue; +class QTcpSocket; +class QTcpServer; + +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/main.cpp b/main.cpp new file mode 100644 index 0000000..1d55979 --- /dev/null +++ b/main.cpp @@ -0,0 +1,12 @@ +#include <QtGui> +#include <QtScript> +#include <QtNetwork> +#include "qscriptremotetargetdebugger.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + QScriptRemoteTargetDebugger debugger; + debugger.attachTo(QHostAddress::LocalHost, 2000); + return app.exec(); +} diff --git a/qscriptremotetargetdebugger.cpp b/qscriptremotetargetdebugger.cpp new file mode 100644 index 0000000..eef37ca --- /dev/null +++ b/qscriptremotetargetdebugger.cpp @@ -0,0 +1,575 @@ +#include <QtNetwork> +#include <QtGui> + +#include "qscriptremotetargetdebugger.h" + +#include <private/qscriptdebuggerfrontend_p.h> +#include <private/qscriptdebugger_p.h> +#include <private/qscriptdebuggercommand_p.h> +#include <private/qscriptdebuggerevent_p.h> +#include <private/qscriptdebuggerresponse_p.h> +#include <private/qscriptdebuggerwidgetfactoryinterface_p.h> +#include <private/qscriptdebugoutputwidget_p.h> +#include <private/qscriptdebuggerconsolewidget_p.h> +#include <private/qscripterrorlogwidget_p.h> +#include <private/qscriptbreakpointswidget_p.h> +#include <private/qscriptdebuggercodewidget_p.h> +#include <private/qscriptdebuggercodefinderwidget_p.h> +#include <private/qscriptdebuggerstackwidget_p.h> +#include <private/qscriptdebuggerscriptswidget_p.h> +#include <private/qscriptdebuggerlocalswidget_p.h> + +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"); + qDebug("writing handshake data"); + 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(0); +} + +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) { + qDebug("handshake ok!"); + 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: { + qDebug() << "got something! bytes available:" << m_socket->bytesAvailable(); + 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; + if (m_blockSize == 512) + qDebug() << m_socket->readAll().toHex(); + qDebug() << "blockSize:" << m_blockSize; + } + if (m_socket->bytesAvailable() < m_blockSize) { + qDebug("waiting for more..."); + return; + } + + int wasAvailable = m_socket->bytesAvailable(); + quint8 type; + in >> type; + if (type == 0) { + // event + qDebug() << "deserializing event"; + QScriptDebuggerEvent event(QScriptDebuggerEvent::None); + in >> event; + qDebug() << "notifying event of type" << event.type(); + notifyEvent(event); + } else { + // command response + qDebug() << "deserializing command response"; + qint32 id; + in >> id; + QScriptDebuggerResponse response; + in >> response; + qDebug() << "notifying command" << id << "finished"; + notifyCommandFinished((int)id, response); + } + Q_ASSERT(m_socket->bytesAvailable() == wasAvailable - m_blockSize); + m_blockSize = 0; + qDebug() << "bytes available is now" << m_socket->bytesAvailable(); + 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)); + qDebug("writing command (id=%d, %d bytes)", id, block.size()); + 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<QScriptRemoteTargetDebugger*>(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<QScriptRemoteTargetDebugger*>(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<QScriptRemoteTargetDebugger*>(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<QScriptRemoteTargetDebugger*>(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 new file mode 100644 index 0000000..f60a709 --- /dev/null +++ b/qscriptremotetargetdebugger.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTREMOTETARGETDEBUGGER_H +#define QSCRIPTREMOTETARGETDEBUGGER_H + +#include <QtCore/qobject.h> + +#include <private/qscriptdebuggerwidgetfactoryinterface_p.h> + +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 new file mode 100644 index 0000000..a9124c9 --- /dev/null +++ b/remotescriptdebugger.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +QT += script scripttools network +# Input +SOURCES += main.cpp +SOURCES += qscriptremotetargetdebugger.cpp +HEADERS += qscriptremotetargetdebugger.h |