summaryrefslogtreecommitdiffstats
path: root/src/qscriptremotetargetdebugger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qscriptremotetargetdebugger.cpp')
-rw-r--r--src/qscriptremotetargetdebugger.cpp596
1 files changed, 596 insertions, 0 deletions
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 <QtNetwork/qtcpsocket.h>
+#include <QtGui>
+
+#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>
+
+// #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<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"