diff --git a/src/libs/qmljsdebugclient/qdeclarativedebugclient_p.h b/src/libs/qmljsdebugclient/qdeclarativedebugclient_p.h index bbbe19b2a54c8f354aa6d886807a538c72fccec8..c87ccef67fd87b0d4659ea3a5c58cbc059fb0f6f 100644 --- a/src/libs/qmljsdebugclient/qdeclarativedebugclient_p.h +++ b/src/libs/qmljsdebugclient/qdeclarativedebugclient_p.h @@ -88,7 +88,7 @@ public: Status status() const; - void sendMessage(const QByteArray &); + virtual void sendMessage(const QByteArray &); protected: virtual void statusChanged(Status); diff --git a/src/plugins/debugger/gdb/codagdbadapter.cpp b/src/plugins/debugger/gdb/codagdbadapter.cpp index d28dda19f9cabdc76c062de9617ddcf2ad9f9bde..cedf750cf521e66eae4af09418d951eafa1fa283 100644 --- a/src/plugins/debugger/gdb/codagdbadapter.cpp +++ b/src/plugins/debugger/gdb/codagdbadapter.cpp @@ -1349,7 +1349,7 @@ void CodaGdbAdapter::handleReadRegisters(const CodaCommandResult &result) logMessage("ERROR: " + result.errorString(), LogError); return; } - if (result.values.isEmpty() || result.values.front().type() != JsonValue::String) { + if (result.values.isEmpty() || result.values.front().type() != Json::JsonValue::String) { logMessage(_("Format error in register message: ") + result.toString(), LogError); return; diff --git a/src/plugins/debugger/qml/qml.pri b/src/plugins/debugger/qml/qml.pri index e9621a0c92ad8055793e9f274f20aea62d6fbe5f..0cec0d1438e5506946ec097950b331d35175ebb6 100644 --- a/src/plugins/debugger/qml/qml.pri +++ b/src/plugins/debugger/qml/qml.pri @@ -1,4 +1,6 @@ include($$PWD/../../../libs/qmljsdebugclient/qmljsdebugclient-lib.pri) +include($$PWD/../../../shared/json/json.pri) +DEFINES += JSON_INCLUDE_PRI HEADERS += \ $$PWD/qmlengine.h \ @@ -6,10 +8,15 @@ HEADERS += \ $$PWD/qmldebuggerclient.h \ $$PWD/qmljsprivateapi.h \ $$PWD/qmlcppengine.h \ - $$PWD/scriptconsole.h + $$PWD/scriptconsole.h \ + $$PWD/qscriptdebuggerclient.h \ + $$PWD/qmlv8debuggerclient.h + SOURCES += \ $$PWD/qmlengine.cpp \ $$PWD/qmladapter.cpp \ $$PWD/qmldebuggerclient.cpp \ $$PWD/qmlcppengine.cpp \ - $$PWD/scriptconsole.cpp + $$PWD/scriptconsole.cpp \ + $$PWD/qscriptdebuggerclient.cpp \ + $$PWD/qmlv8debuggerclient.cpp diff --git a/src/plugins/debugger/qml/qmladapter.cpp b/src/plugins/debugger/qml/qmladapter.cpp index d131c7ba53d737f87f9204a2183aa1e8bfed433b..4ec0e417a5ae2ee32278a9208187cb8c83f592ee 100644 --- a/src/plugins/debugger/qml/qmladapter.cpp +++ b/src/plugins/debugger/qml/qmladapter.cpp @@ -33,10 +33,11 @@ #include "qmladapter.h" #include "debuggerstartparameters.h" -#include "qmldebuggerclient.h" +#include "qscriptdebuggerclient.h" +#include "qmlv8debuggerclient.h" #include "qmljsprivateapi.h" -#include "debuggerengine.h" +#include "qmlengine.h" #include <extensionsystem/pluginmanager.h> #include <utils/qtcassert.h> @@ -61,13 +62,13 @@ public: } QWeakPointer<DebuggerEngine> m_engine; - Internal::QmlDebuggerClient *m_qmlClient; + QmlDebuggerClient *m_qmlClient; QTimer m_connectionTimer; int m_connectionAttempts; int m_maxConnectionAttempts; QDeclarativeDebugConnection *m_conn; - QList<QByteArray> sendBuffer; + QHash<QString, QmlDebuggerClient*> debugClients; }; } // namespace Internal @@ -149,16 +150,6 @@ void QmlAdapter::connectToViewer() } } -void QmlAdapter::sendMessage(const QByteArray &msg) -{ - if (d->m_qmlClient->status() == QDeclarativeDebugClient::Enabled) { - flushSendBuffer(); - d->m_qmlClient->sendMessage(msg); - } else { - d->sendBuffer.append(msg); - } -} - void QmlAdapter::connectionErrorOccurred(QAbstractSocket::SocketError socketError) { showConnectionStatusMessage(tr("Error: (%1) %2", "%1=error code, %2=error message") @@ -177,8 +168,10 @@ void QmlAdapter::clientStatusChanged(QDeclarativeDebugClient::Status status) logServiceStatusChange(serviceName, status); - if (status == QDeclarativeDebugClient::Enabled) - flushSendBuffer(); + if (status == QDeclarativeDebugClient::Enabled) { + d->m_qmlClient = d->debugClients.value(serviceName); + d->m_qmlClient->flushSendBuffer(); + } } void QmlAdapter::connectionStateChanged() @@ -202,7 +195,8 @@ void QmlAdapter::connectionStateChanged() showConnectionStatusMessage(tr("connected.\n")); if (!d->m_qmlClient) - createDebuggerClient(); + createDebuggerClients(); + //reloadEngines(); emit connected(); break; @@ -216,16 +210,23 @@ void QmlAdapter::connectionStateChanged() } } -void QmlAdapter::createDebuggerClient() +void QmlAdapter::createDebuggerClients() { - d->m_qmlClient = new Internal::QmlDebuggerClient(d->m_conn); - connect(d->m_qmlClient, SIGNAL(newStatus(QDeclarativeDebugClient::Status)), + Internal::QScriptDebuggerClient *client1 = new Internal::QScriptDebuggerClient(d->m_conn); + connect(client1, SIGNAL(newStatus(QDeclarativeDebugClient::Status)), this, SLOT(clientStatusChanged(QDeclarativeDebugClient::Status))); - connect(d->m_engine.data(), SIGNAL(sendMessage(QByteArray)), - this, SLOT(sendMessage(QByteArray))); - connect(d->m_qmlClient, SIGNAL(messageWasReceived(QByteArray)), - d->m_engine.data(), SLOT(messageReceived(QByteArray))); + + Internal::QmlV8DebuggerClient *client2 = new Internal::QmlV8DebuggerClient(d->m_conn); + connect(client2, SIGNAL(newStatus(QDeclarativeDebugClient::Status)), + this, SLOT(clientStatusChanged(QDeclarativeDebugClient::Status))); + + d->debugClients.insert(client1->name(),client1); + d->debugClients.insert(client2->name(),client2); + + + client1->setEngine((Internal::QmlEngine*)(d->m_engine.data())); + client2->setEngine((Internal::QmlEngine*)(d->m_engine.data())); //engine->startSuccessful(); // FIXME: AAA: port to new debugger states } @@ -243,13 +244,13 @@ QDeclarativeDebugConnection *QmlAdapter::connection() const void QmlAdapter::showConnectionStatusMessage(const QString &message) { if (!d->m_engine.isNull()) - d->m_engine.data()->showMessage(QLatin1String("QmlJSDebugger: ") + message, LogStatus); + d->m_engine.data()->showMessage(QLatin1String("QmlDebugger: ") + message, LogStatus); } void QmlAdapter::showConnectionErrorMessage(const QString &message) { if (!d->m_engine.isNull()) - d->m_engine.data()->showMessage(QLatin1String("QmlJSDebugger: ") + message, LogError); + d->m_engine.data()->showMessage(QLatin1String("QmlDebugger: ") + message, LogError); } bool QmlAdapter::disableJsDebugging(bool block) @@ -271,6 +272,15 @@ bool QmlAdapter::disableJsDebugging(bool block) return isBlocked; } +Internal::QmlDebuggerClient *QmlAdapter::activeDebuggerClient() +{ + return d->m_qmlClient; +} + +QHash<QString, Internal::QmlDebuggerClient*> QmlAdapter::debuggerClients() +{ + return d->debugClients; +} void QmlAdapter::logServiceStatusChange(const QString &service, QDeclarativeDebugClient::Status newStatus) { @@ -298,12 +308,4 @@ void QmlAdapter::logServiceActivity(const QString &service, const QString &logMe d->m_engine.data()->showMessage(QString("%1 %2").arg(service, logMessage), LogDebug); } -void QmlAdapter::flushSendBuffer() -{ - QTC_ASSERT(d->m_qmlClient->status() == QDeclarativeDebugClient::Enabled, return); - foreach (const QByteArray &msg, d->sendBuffer) - d->m_qmlClient->sendMessage(msg); - d->sendBuffer.clear(); -} - } // namespace Debugger diff --git a/src/plugins/debugger/qml/qmladapter.h b/src/plugins/debugger/qml/qmladapter.h index e23020a621b937bfd9f52726e5751f39a0204fba..07b67ada7f983234786fa51b3b3c8f958a0d042d 100644 --- a/src/plugins/debugger/qml/qmladapter.h +++ b/src/plugins/debugger/qml/qmladapter.h @@ -70,6 +70,9 @@ public: bool disableJsDebugging(bool block); + Internal::QmlDebuggerClient *activeDebuggerClient(); + QHash<QString, Internal::QmlDebuggerClient*> debuggerClients(); + public slots: void logServiceStatusChange(const QString &service, QDeclarativeDebugClient::Status newStatus); void logServiceActivity(const QString &service, const QString &logMessage); @@ -82,7 +85,6 @@ signals: void serviceConnectionError(const QString serviceName); private slots: - void sendMessage(const QByteArray &msg); void connectionErrorOccurred(QAbstractSocket::SocketError socketError); void clientStatusChanged(QDeclarativeDebugClient::Status status); void connectionStateChanged(); @@ -90,10 +92,9 @@ private slots: private: void connectToViewer(); - void createDebuggerClient(); + void createDebuggerClients(); void showConnectionStatusMessage(const QString &message); void showConnectionErrorMessage(const QString &message); - void flushSendBuffer(); private: QScopedPointer<Internal::QmlAdapterPrivate> d; diff --git a/src/plugins/debugger/qml/qmldebuggerclient.cpp b/src/plugins/debugger/qml/qmldebuggerclient.cpp index 2563ecab0f3710d833c20b8d3724952737dfe9f9..68b1cc1f0a8d09fcc6cbeca3b3326bee856ab74d 100644 --- a/src/plugins/debugger/qml/qmldebuggerclient.cpp +++ b/src/plugins/debugger/qml/qmldebuggerclient.cpp @@ -37,13 +37,21 @@ namespace Debugger { namespace Internal { -QmlDebuggerClient::QmlDebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection* client) - : QDeclarativeDebugClient(QLatin1String("JSDebugger"), client) +class QmlDebuggerClientPrivate +{ +public: + QList<QByteArray> sendBuffer; +}; + +QmlDebuggerClient::QmlDebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection* client, QLatin1String clientName) + : QDeclarativeDebugClient(clientName, client), + d(new QmlDebuggerClientPrivate()) { } QmlDebuggerClient::~QmlDebuggerClient() { + delete d; } void QmlDebuggerClient::statusChanged(Status status) @@ -51,9 +59,21 @@ void QmlDebuggerClient::statusChanged(Status status) emit newStatus(status); } -void QmlDebuggerClient::messageReceived(const QByteArray &data) +void QmlDebuggerClient::sendMessage(const QByteArray &msg) +{ + if (status() == Enabled) { + QDeclarativeDebugClient::sendMessage(msg); + } else { + d->sendBuffer.append(msg); + } +} + +void QmlDebuggerClient::flushSendBuffer() { - emit messageWasReceived(data); + QTC_ASSERT(status() == QDeclarativeDebugClient::Enabled, return); + foreach (const QByteArray &msg, d->sendBuffer) + QDeclarativeDebugClient::sendMessage(msg); + d->sendBuffer.clear(); } } // Internal diff --git a/src/plugins/debugger/qml/qmldebuggerclient.h b/src/plugins/debugger/qml/qmldebuggerclient.h index 191858e3667fbbeb64c741e100be2a324b8ac9db..a1ceeafac52c5a6ca46b57b4357edb8e9da4c3e0 100644 --- a/src/plugins/debugger/qml/qmldebuggerclient.h +++ b/src/plugins/debugger/qml/qmldebuggerclient.h @@ -30,32 +30,70 @@ ** **************************************************************************/ -#ifndef QMLJSDEBUGGERCLIENT_H -#define QMLJSDEBUGGERCLIENT_H +#ifndef QMLDEBUGGERCLIENT_H +#define QMLDEBUGGERCLIENT_H #include "qmljsprivateapi.h" namespace Debugger { namespace Internal { +class WatchData; +class BreakHandler; +class BreakpointModelId; +class QmlEngine; +class QmlDebuggerClientPrivate; + class QmlDebuggerClient : public QDeclarativeDebugClient { Q_OBJECT public: - QmlDebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection *client); + QmlDebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection* client, QLatin1String clientName); virtual ~QmlDebuggerClient(); + virtual void executeStep() = 0; + virtual void executeStepOut() = 0; + virtual void executeNext() = 0; + virtual void executeStepI() = 0; + + virtual void continueInferior() = 0; + virtual void interruptInferior() = 0; + + virtual void activateFrame(int index) = 0; + + virtual void insertBreakpoints(BreakHandler *handler, BreakpointModelId *id) = 0; + virtual void removeBreakpoints(BreakpointModelId *id) = 0; + virtual void setBreakpoints() = 0; + + virtual void assignValueInDebugger(const QByteArray expr, const quint64 &id, + const QString &property, const QString value) = 0; + + virtual void updateWatchData(const WatchData *data) = 0; + virtual void executeDebuggerCommand(const QString &command) = 0; + + virtual void synchronizeWatchers(const QStringList &watchers) = 0; + + virtual void expandObject(const QByteArray &iname, quint64 objectId) = 0; + virtual void sendPing() = 0; + + virtual void setEngine(QmlEngine *engine) = 0; + + void flushSendBuffer(); + signals: void newStatus(QDeclarativeDebugClient::Status status); - void messageWasReceived(const QByteArray &data); protected: virtual void statusChanged(Status status); - virtual void messageReceived(const QByteArray &data); + void sendMessage(const QByteArray &msg); + +private: + QmlDebuggerClientPrivate *d; + friend class QmlDebuggerClientPrivate; }; } // Internal -} // QmlJSInspector +} // Debugger -#endif // QMLJSDEBUGGERCLIENT_H +#endif // QMLDEBUGGERCLIENT_H diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index ecac3a133d620bec28604f704ec4c66948817999..7b7aa9e4b0b9d3f2d7b9ad653176d01530bcd5f5 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -89,70 +89,6 @@ using namespace ProjectExplorer; namespace Debugger { namespace Internal { -struct JSAgentBreakpointData -{ - QByteArray functionName; - QByteArray fileUrl; - qint32 lineNumber; -}; - -struct JSAgentStackData -{ - QByteArray functionName; - QByteArray fileUrl; - qint32 lineNumber; -}; - -uint qHash(const JSAgentBreakpointData &b) -{ - return b.lineNumber ^ qHash(b.fileUrl); -} - -QDataStream &operator<<(QDataStream &s, const JSAgentBreakpointData &data) -{ - return s << data.functionName << data.fileUrl << data.lineNumber; -} - -QDataStream &operator<<(QDataStream &s, const JSAgentStackData &data) -{ - return s << data.functionName << data.fileUrl << data.lineNumber; -} - -QDataStream &operator>>(QDataStream &s, JSAgentBreakpointData &data) -{ - return s >> data.functionName >> data.fileUrl >> data.lineNumber; -} - -QDataStream &operator>>(QDataStream &s, JSAgentStackData &data) -{ - return s >> data.functionName >> data.fileUrl >> data.lineNumber; -} - -bool operator==(const JSAgentBreakpointData &b1, const JSAgentBreakpointData &b2) -{ - return b1.lineNumber == b2.lineNumber && b1.fileUrl == b2.fileUrl; -} - -typedef QSet<JSAgentBreakpointData> JSAgentBreakpoints; -typedef QList<JSAgentStackData> JSAgentStackFrames; - - -static QDataStream &operator>>(QDataStream &s, WatchData &data) -{ - data = WatchData(); - QByteArray name; - QByteArray value; - QByteArray type; - bool hasChildren = false; - s >> data.exp >> name >> value >> type >> hasChildren >> data.id; - data.name = QString::fromUtf8(name); - data.setType(type, false); - data.setValue(QString::fromUtf8(value)); - data.setHasChildren(hasChildren); - data.setAllUnneeded(); - return s; -} - class QmlEnginePrivate { public: @@ -160,7 +96,6 @@ public: private: friend class QmlEngine; - int m_ping; QmlAdapter m_adapter; ApplicationLauncher m_applicationLauncher; Utils::FileInProjectFinder fileFinder; @@ -168,7 +103,7 @@ private: }; QmlEnginePrivate::QmlEnginePrivate(QmlEngine *q) - : m_ping(0), m_adapter(q) + : m_adapter(q) {} @@ -466,8 +401,6 @@ void QmlEngine::shutdownEngine() void QmlEngine::setupEngine() { - d->m_ping = 0; - connect(&d->m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)), runControl(), SLOT(bringApplicationToForeground(qint64)), Qt::UniqueConnection); @@ -478,12 +411,8 @@ void QmlEngine::setupEngine() void QmlEngine::continueInferior() { QTC_ASSERT(state() == InferiorStopOk, qDebug() << state()); - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "CONTINUE"; - rs << cmd; - logMessage(LogSend, cmd); - sendMessage(reply); + logMessage(LogSend, "CONTINUE"); + d->m_adapter.activeDebuggerClient()->continueInferior(); resetLocation(); notifyInferiorRunRequested(); notifyInferiorRunOk(); @@ -491,23 +420,15 @@ void QmlEngine::continueInferior() void QmlEngine::interruptInferior() { - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "INTERRUPT"; - rs << cmd; - logMessage(LogSend, cmd); - sendMessage(reply); - notifyInferiorStopOk(); + logMessage(LogSend, "INTERRUPT"); + d->m_adapter.activeDebuggerClient()->interruptInferior(); + } void QmlEngine::executeStep() { - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "STEPINTO"; - rs << cmd; - logMessage(LogSend, cmd); - sendMessage(reply); + logMessage(LogSend, "STEPINTO"); + d->m_adapter.activeDebuggerClient()->executeStep(); resetLocation(); notifyInferiorRunRequested(); notifyInferiorRunOk(); @@ -515,12 +436,8 @@ void QmlEngine::executeStep() void QmlEngine::executeStepI() { - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "STEPINTO"; - rs << cmd; - logMessage(LogSend, cmd); - sendMessage(reply); + logMessage(LogSend, "STEPINTO"); + d->m_adapter.activeDebuggerClient()->executeStepI(); resetLocation(); notifyInferiorRunRequested(); notifyInferiorRunOk(); @@ -528,12 +445,8 @@ void QmlEngine::executeStepI() void QmlEngine::executeStepOut() { - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "STEPOUT"; - rs << cmd; - logMessage(LogSend, cmd); - sendMessage(reply); + logMessage(LogSend, "STEPOUT"); + d->m_adapter.activeDebuggerClient()->executeStepOut(); resetLocation(); notifyInferiorRunRequested(); notifyInferiorRunOk(); @@ -541,12 +454,8 @@ void QmlEngine::executeStepOut() void QmlEngine::executeNext() { - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "STEPOVER"; - rs << cmd; - logMessage(LogSend, cmd); - sendMessage(reply); + logMessage(LogSend, "STEPOVER"); + d->m_adapter.activeDebuggerClient()->executeNext(); resetLocation(); notifyInferiorRunRequested(); notifyInferiorRunOk(); @@ -577,13 +486,8 @@ void QmlEngine::executeJumpToLine(const ContextData &data) void QmlEngine::activateFrame(int index) { - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "ACTIVATE_FRAME"; - rs << cmd - << index; - logMessage(LogSend, QString("%1 %2").arg(QString(cmd), QString::number(index))); - sendMessage(reply); + logMessage(LogSend, QString("%1 %2").arg(QString("ACTIVATE_FRAME"), QString::number(index))); + d->m_adapter.activeDebuggerClient()->activateFrame(index); gotoLocation(stackHandler()->frames().value(index)); } @@ -602,40 +506,46 @@ void QmlEngine::attemptBreakpointSynchronization() handler->setEngine(id, this); } - JSAgentBreakpoints breakpoints; + QStringList breakPointsStr; foreach (BreakpointModelId id, handler->engineBreakpointIds(this)) { if (handler->state(id) == BreakpointRemoveRequested) { handler->notifyBreakpointRemoveProceeding(id); + if (d->m_adapter.activeDebuggerClient()) + d->m_adapter.activeDebuggerClient()->removeBreakpoints(&id); + else { + foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) { + client->removeBreakpoints(&id); + } + } handler->notifyBreakpointRemoveOk(id); } else { if (handler->state(id) == BreakpointInsertRequested) { handler->notifyBreakpointInsertProceeding(id); } - JSAgentBreakpointData bp; - bp.fileUrl = QUrl::fromLocalFile(handler->fileName(id)).toString().toUtf8(); - bp.lineNumber = handler->lineNumber(id); - bp.functionName = handler->functionName(id).toUtf8(); - breakpoints.insert(bp); + if (d->m_adapter.activeDebuggerClient()) + d->m_adapter.activeDebuggerClient()->insertBreakpoints(handler,&id); + else { + foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) { + client->insertBreakpoints(handler,&id); + } + } if (handler->state(id) == BreakpointInsertProceeding) { handler->notifyBreakpointInsertOk(id); } + breakPointsStr << QString("('%1' '%2' %3)").arg(QString(handler->functionName(id).toUtf8()), + QString(QUrl::fromLocalFile(handler->fileName(id)).toString().toUtf8()), QString::number(handler->lineNumber(id))); } } - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "BREAKPOINTS"; - rs << cmd - << breakpoints; + logMessage(LogSend, QString("%1 [%2]").arg(QString("BREAKPOINTS"), breakPointsStr.join(", "))); - QStringList breakPointsStr; - foreach (const JSAgentBreakpointData &bp, breakpoints) { - breakPointsStr << QString("('%1' '%2' %3)").arg(QString(bp.functionName), - QString(bp.fileUrl), QString::number(bp.lineNumber)); + if (d->m_adapter.activeDebuggerClient()) { + d->m_adapter.activeDebuggerClient()->setBreakpoints(); + } + else { + foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) + client->setBreakpoints(); } - logMessage(LogSend, QString("%1 [%2]").arg(QString(cmd), breakPointsStr.join(", "))); - - sendMessage(reply); } bool QmlEngine::acceptsBreakpoint(BreakpointModelId id) const @@ -682,25 +592,15 @@ bool QmlEngine::setToolTipExpression(const QPoint &mousePos, // ////////////////////////////////////////////////////////////////////// -void QmlEngine::assignValueInDebugger(const WatchData *, +void QmlEngine::assignValueInDebugger(const WatchData *data, const QString &expression, const QVariant &valueV) { - QRegExp inObject("@([0-9a-fA-F]+)->(.+)"); - if (inObject.exactMatch(expression)) { - bool ok = false; - quint64 objectId = inObject.cap(1).toULongLong(&ok, 16); - QString property = inObject.cap(2); - if (ok && objectId > 0 && !property.isEmpty()) { - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "SET_PROPERTY"; - rs << cmd; - rs << expression.toUtf8() << objectId << property << valueV.toString(); - logMessage(LogSend, QString("%1 %2 %3 %4 %5").arg( - QString(cmd), QString::number(objectId), QString(property), - valueV.toString())); - sendMessage(reply); - } + quint64 objectId = data->id; + if (objectId > 0 && !expression.isEmpty()) { + logMessage(LogSend, QString("%1 %2 %3 %4 %5").arg( + QString("SET_PROPERTY"), QString::number(objectId), QString(expression), + valueV.toString())); + d->m_adapter.activeDebuggerClient()->assignValueInDebugger(expression.toUtf8(), objectId, expression, valueV.toString()); } } @@ -712,19 +612,14 @@ void QmlEngine::updateWatchData(const WatchData &data, showStatusMessage(tr("Stopped."), 5000); if (!data.name.isEmpty() && data.isValueNeeded()) { - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "EXEC"; - rs << cmd; - rs << data.iname << data.name; - logMessage(LogSend, QString("%1 %2 %3").arg(QString(cmd), QString(data.iname), + logMessage(LogSend, QString("%1 %2 %3").arg(QString("EXEC"), QString(data.iname), QString(data.name))); - sendMessage(reply); + d->m_adapter.activeDebuggerClient()->updateWatchData(&data); } if (!data.name.isEmpty() && data.isChildrenNeeded() && watchHandler()->isExpandedIName(data.iname)) { - expandObject(data.iname, data.id); + d->m_adapter.activeDebuggerClient()->expandObject(data.iname, data.id); } synchronizeWatchers(); @@ -735,39 +630,17 @@ void QmlEngine::updateWatchData(const WatchData &data, void QmlEngine::synchronizeWatchers() { + QStringList watchedExpressions = watchHandler()->watchedExpressions(); // send watchers list - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "WATCH_EXPRESSIONS"; - rs << cmd; - rs << watchHandler()->watchedExpressions(); logMessage(LogSend, QString("%1 %2").arg( - QString(cmd), watchHandler()->watchedExpressions().join(", "))); - sendMessage(reply); -} - -void QmlEngine::expandObject(const QByteArray &iname, quint64 objectId) -{ - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "EXPAND"; - rs << cmd; - rs << iname << objectId; - logMessage(LogSend, QString("%1 %2 %3").arg(QString(cmd), QString(iname), - QString::number(objectId))); - sendMessage(reply); -} - -void QmlEngine::sendPing() -{ - d->m_ping++; - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "PING"; - rs << cmd; - rs << d->m_ping; - logMessage(LogSend, QString("%1 %2").arg(QString(cmd), QString::number(d->m_ping))); - sendMessage(reply); + QString("WATCH_EXPRESSIONS"), watchedExpressions.join(", "))); + if (d->m_adapter.activeDebuggerClient()) { + d->m_adapter.activeDebuggerClient()->synchronizeWatchers(watchedExpressions); + } + else { + foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) + client->synchronizeWatchers(watchedExpressions); + } } unsigned QmlEngine::debuggerCapabilities() const @@ -794,209 +667,12 @@ QString QmlEngine::toFileInProject(const QUrl &fileUrl) return d->fileFinder.findFile(fileUrl); } -void QmlEngine::messageReceived(const QByteArray &message) +void QmlEngine::inferiorSpontaneousStop() { - QByteArray rwData = message; - QDataStream stream(&rwData, QIODevice::ReadOnly); - - QByteArray command; - stream >> command; - - if (command == "STOPPED") { - //qDebug() << command << this << state(); - if (state() == InferiorRunOk) - notifyInferiorSpontaneousStop(); - - QString logString = QString::fromLatin1(command); - - JSAgentStackFrames stackFrames; - QList<WatchData> watches; - QList<WatchData> locals; - stream >> stackFrames >> watches >> locals; - - logString += QString::fromLatin1(" (%1 stack frames) (%2 watches) (%3 locals)"). - arg(stackFrames.size()).arg(watches.size()).arg(locals.size()); - - StackFrames ideStackFrames; - for (int i = 0; i != stackFrames.size(); ++i) { - StackFrame frame; - frame.line = stackFrames.at(i).lineNumber; - frame.function = stackFrames.at(i).functionName; - frame.file = toFileInProject(QUrl(stackFrames.at(i).fileUrl)); - frame.usable = QFileInfo(frame.file).isReadable(); - frame.level = i + 1; - ideStackFrames << frame; - } - - if (ideStackFrames.size() && ideStackFrames.back().function == "<global>") - ideStackFrames.takeLast(); - stackHandler()->setFrames(ideStackFrames); - - watchHandler()->beginCycle(); - bool needPing = false; - - foreach (WatchData data, watches) { - data.iname = watchHandler()->watcherName(data.exp); - watchHandler()->insertData(data); - - if (watchHandler()->expandedINames().contains(data.iname)) { - needPing = true; - expandObject(data.iname, data.id); - } - } - - foreach (WatchData data, locals) { - data.iname = "local." + data.exp; - watchHandler()->insertData(data); - - if (watchHandler()->expandedINames().contains(data.iname)) { - needPing = true; - expandObject(data.iname, data.id); - } - } - - if (needPing) { - sendPing(); - } else { - watchHandler()->endCycle(); - } - - bool becauseOfException; - stream >> becauseOfException; - - logString += becauseOfException ? " exception" : " no_exception"; - - if (becauseOfException) { - QString error; - stream >> error; - - logString += QLatin1Char(' '); - logString += error; - logMessage(LogReceive, logString); - - QString msg = stackFrames.isEmpty() - ? tr("<p>An uncaught exception occurred:</p><p>%1</p>") - .arg(Qt::escape(error)) - : tr("<p>An uncaught exception occurred in <i>%1</i>:</p><p>%2</p>") - .arg(stackFrames.value(0).fileUrl, Qt::escape(error)); - showMessageBox(QMessageBox::Information, tr("Uncaught Exception"), msg); - } else { - // - // Make breakpoint non-pending - // - QString file; - QString function; - int line = -1; - - if (!ideStackFrames.isEmpty()) { - file = ideStackFrames.at(0).file; - line = ideStackFrames.at(0).line; - function = ideStackFrames.at(0).function; - } - - BreakHandler *handler = breakHandler(); - foreach (BreakpointModelId id, handler->engineBreakpointIds(this)) { - QString processedFilename = handler->fileName(id); - if (processedFilename == file && handler->lineNumber(id) == line) { - QTC_CHECK(handler->state(id) == BreakpointInserted); - BreakpointResponse br = handler->response(id); - br.fileName = file; - br.lineNumber = line; - br.functionName = function; - handler->setResponse(id, br); - } - } - - logMessage(LogReceive, logString); - } - - if (!ideStackFrames.isEmpty()) - gotoLocation(ideStackFrames.value(0)); - - } else if (command == "RESULT") { - WatchData data; - QByteArray iname; - stream >> iname >> data; - - logMessage(LogReceive, QString("%1 %2 %3").arg(QString(command), - QString(iname), QString(data.value))); - data.iname = iname; - if (iname.startsWith("watch.")) { - watchHandler()->insertData(data); - } else if(iname == "console") { - showMessage(data.value, ScriptConsoleOutput); - } else { - qWarning() << "QmlEngine: Unexcpected result: " << iname << data.value; - } - } else if (command == "EXPANDED") { - QList<WatchData> result; - QByteArray iname; - stream >> iname >> result; - - logMessage(LogReceive, QString("%1 %2 (%3 x watchdata)").arg( - QString(command), QString(iname), QString::number(result.size()))); - bool needPing = false; - foreach (WatchData data, result) { - data.iname = iname + '.' + data.exp; - watchHandler()->insertData(data); - - if (watchHandler()->expandedINames().contains(data.iname)) { - needPing = true; - expandObject(data.iname, data.id); - } - } - if (needPing) - sendPing(); - } else if (command == "LOCALS") { - QList<WatchData> locals; - QList<WatchData> watches; - int frameId; - stream >> frameId >> locals; - if (!stream.atEnd()) { // compatibility with jsdebuggeragent from 2.1, 2.2 - stream >> watches; - } - - logMessage(LogReceive, QString("%1 %2 (%3 x locals) (%4 x watchdata)").arg( - QString(command), QString::number(frameId), - QString::number(locals.size()), - QString::number(watches.size()))); - watchHandler()->beginCycle(); - bool needPing = false; - foreach (WatchData data, watches) { - data.iname = watchHandler()->watcherName(data.exp); - watchHandler()->insertData(data); - - if (watchHandler()->expandedINames().contains(data.iname)) { - needPing = true; - expandObject(data.iname, data.id); - } - } - - foreach (WatchData data, locals) { - data.iname = "local." + data.exp; - watchHandler()->insertData(data); - if (watchHandler()->expandedINames().contains(data.iname)) { - needPing = true; - expandObject(data.iname, data.id); - } - } - if (needPing) - sendPing(); - else - watchHandler()->endCycle(); - - } else if (command == "PONG") { - int ping; - stream >> ping; - - logMessage(LogReceive, QString("%1 %2").arg(QString(command), QString::number(ping))); - - if (ping == d->m_ping) - watchHandler()->endCycle(); - } else { - qDebug() << Q_FUNC_INFO << "Unknown command: " << command; - logMessage(LogReceive, QString("%1 UNKNOWN COMMAND!!").arg(QString(command))); - } + if (state() == InferiorRunOk) + notifyInferiorSpontaneousStop(); + else + notifyInferiorStopOk(); } void QmlEngine::disconnected() @@ -1016,14 +692,9 @@ void QmlEngine::wrongSetupMessageBoxFinished(int result) void QmlEngine::executeDebuggerCommand(const QString& command) { - QByteArray reply; - QDataStream rs(&reply, QIODevice::WriteOnly); - QByteArray cmd = "EXEC"; - QByteArray console = "console"; - rs << cmd << console << command; - logMessage(LogSend, QString("%1 %2 %3").arg(QString(cmd), QString(console), + logMessage(LogSend, QString("%1 %2 %3").arg(QString("EXEC"), QString("console"), QString(command))); - sendMessage(reply); + d->m_adapter.activeDebuggerClient()->executeDebuggerCommand(command); } @@ -1034,7 +705,7 @@ QString QmlEngine::qmlImportPath() const void QmlEngine::logMessage(LogDirection direction, const QString &message) { - QString msg = "JSDebugger"; + QString msg = "QmlDebugger"; if (direction == LogSend) { msg += " sending "; } else { diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h index 904a301832ebb476d7015bec569505fae85766c4..2658de726a8c01a56cf054137a5df45529b11f5d 100644 --- a/src/plugins/debugger/qml/qmlengine.h +++ b/src/plugins/debugger/qml/qmlengine.h @@ -49,6 +49,11 @@ class QmlEngine : public DebuggerEngine Q_OBJECT public: + enum LogDirection { + LogSend, + LogReceive + }; + QmlEngine(const DebuggerStartParameters &startParameters, DebuggerEngine *masterEngine); ~QmlEngine(); @@ -62,9 +67,12 @@ public: int timeout = -1) const; void filterApplicationMessage(const QString &msg, int channel); virtual bool acceptsWatchesWhileRunning() const; + QString toFileInProject(const QUrl &fileUrl); + void inferiorSpontaneousStop(); + + void logMessage(LogDirection direction, const QString &str); public slots: - void messageReceived(const QByteArray &message); void disconnected(); private slots: @@ -120,7 +128,6 @@ private: unsigned int debuggerCapabilities() const; signals: - void sendMessage(const QByteArray &msg); void tooltipRequested(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos); @@ -135,9 +142,6 @@ private slots: void synchronizeWatchers(); private: - void expandObject(const QByteArray &iname, quint64 objectId); - void sendPing(); - void closeConnection(); void startApplicationLauncher(); void stopApplicationLauncher(); @@ -148,13 +152,6 @@ private: const QString &oldBasePath, const QString &newBasePath) const; QString qmlImportPath() const; - enum LogDirection { - LogSend, - LogReceive - }; - void logMessage(LogDirection direction, const QString &str); - QString toFileInProject(const QUrl &fileUrl); - private: friend class QmlCppEngine; QmlEnginePrivate *d; diff --git a/src/plugins/debugger/qml/qmlv8debuggerclient.cpp b/src/plugins/debugger/qml/qmlv8debuggerclient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..38c523af2df726a30c60633d5f06a1f266343da8 --- /dev/null +++ b/src/plugins/debugger/qml/qmlv8debuggerclient.cpp @@ -0,0 +1,624 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at info@qt.nokia.com. +** +**************************************************************************/ + +#include "qmlv8debuggerclient.h" + +#include "watchdata.h" +#include "watchhandler.h" +#include "breakpoint.h" +#include "breakhandler.h" +#include "debuggerconstants.h" +#include "qmlengine.h" +#include "stackhandler.h" +#include "debuggercore.h" + +#include <extensionsystem/pluginmanager.h> +#include <utils/qtcassert.h> + +#include <QtCore/QVariant> +#include <QtCore/QFileInfo> +#include <QtGui/QTextDocument> +#include <QtGui/QMessageBox> + +#define INITIALPARAMS "seq" << ':' << ++d->sequence << ',' << "type" << ':' << "request" + +using namespace Json; + +namespace Debugger { +namespace Internal { + +class QmlV8DebuggerClientPrivate +{ +public: + explicit QmlV8DebuggerClientPrivate(QmlV8DebuggerClient *) : + sequence(0), ping(0), engine(0) + { + + } + + int sequence; + int ping; + QmlEngine *engine; + QHash<BreakpointModelId,int> breakpoints; + QHash<int,BreakpointModelId> breakpointsSync; + QHash<int,QByteArray> locals; + QHash<int,QByteArray> watches; + QByteArray frames; +}; + +QmlV8DebuggerClient::QmlV8DebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection* client) + : QmlDebuggerClient(client, QLatin1String("V8Debugger")), + d(new QmlV8DebuggerClientPrivate(this)) +{ +} + +QmlV8DebuggerClient::~QmlV8DebuggerClient() +{ + delete d; +} + +QByteArray QmlV8DebuggerClient::packMessage(QByteArray& message) +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "V8DEBUG"; + rs << cmd << message; + return reply; +} + +void QmlV8DebuggerClient::executeStep() +{ + QByteArray request; + + JsonInputStream(request) << '{' << INITIALPARAMS ; + JsonInputStream(request) << ',' << "command" << ':' << "continue"; + + JsonInputStream(request) << ',' << "arguments" << ':'; + JsonInputStream(request) << '{' << "stepaction" << ':' << "in"; + JsonInputStream(request) << '}'; + + JsonInputStream(request) << '}'; + + + sendMessage(packMessage(request)); +} + +void QmlV8DebuggerClient::executeStepOut() +{ + QByteArray request; + + JsonInputStream(request) << '{' << INITIALPARAMS ; + JsonInputStream(request) << ',' << "command" << ':' << "continue"; + + JsonInputStream(request) << ',' << "arguments" << ':'; + JsonInputStream(request) << '{' << "stepaction" << ':' << "out"; + JsonInputStream(request) << '}'; + + JsonInputStream(request) << '}'; + + + sendMessage(packMessage(request)); + +} + +void QmlV8DebuggerClient::executeNext() +{ + QByteArray request; + + JsonInputStream(request) << '{' << INITIALPARAMS ; + JsonInputStream(request) << ',' << "command" << ':' << "continue"; + + JsonInputStream(request) << ',' << "arguments" << ':'; + JsonInputStream(request) << '{' << "stepaction" << ':' << "next"; + JsonInputStream(request) << '}'; + + JsonInputStream(request) << '}'; + + + sendMessage(packMessage(request)); + +} + +void QmlV8DebuggerClient::executeStepI() +{ + QByteArray request; + + JsonInputStream(request) << '{' << INITIALPARAMS ; + JsonInputStream(request) << ',' << "command" << ':' << "continue"; + + JsonInputStream(request) << ',' << "arguments" << ':'; + JsonInputStream(request) << '{' << "stepaction" << ':' << "in"; + JsonInputStream(request) << '}'; + + JsonInputStream(request) << '}'; + + + sendMessage(packMessage(request)); + +} + +void QmlV8DebuggerClient::continueInferior() +{ + QByteArray request; + + JsonInputStream(request) << '{' << INITIALPARAMS ; + JsonInputStream(request) << ',' << "command" << ':' << "continue"; + JsonInputStream(request) << '}'; + + + sendMessage(packMessage(request)); + +} + +void QmlV8DebuggerClient::interruptInferior() +{ + QByteArray request; + + JsonInputStream(request) << '{' << INITIALPARAMS ; + JsonInputStream(request) << ',' << "command" << ':' << "interrupt"; + + JsonInputStream(request) << '}'; + + sendMessage(packMessage(request)); + +} + +void QmlV8DebuggerClient::activateFrame(int index) +{ + setLocals(index); +} + +void QmlV8DebuggerClient::insertBreakpoints(BreakHandler *handler, BreakpointModelId *id) +{ + QByteArray request; + + JsonInputStream(request) << '{' << INITIALPARAMS ; + JsonInputStream(request) << ',' << "command" << ':' << "setbreakpoint"; + JsonInputStream(request) << ',' << "arguments" << ':' << '{'; + if (handler->breakpointData(*id).type == BreakpointByFileAndLine) { + JsonInputStream(request) << "type" << ':' << "script"; + JsonInputStream(request) << ',' << "target" << ':' << QFileInfo(handler->fileName(*id)).fileName().toUtf8(); + JsonInputStream(request) << ',' << "line" << ':' << handler->lineNumber(*id) - 1; + } else if (handler->breakpointData(*id).type == BreakpointByFunction) { + JsonInputStream(request) << "type" << ':' << "function"; + JsonInputStream(request) << ',' << "target" << ':' << handler->functionName(*id).toUtf8(); + } + JsonInputStream(request) << '}'; + JsonInputStream(request) << '}'; + + d->breakpointsSync.insert(d->sequence,*id); + sendMessage(packMessage(request)); + +} + +void QmlV8DebuggerClient::removeBreakpoints(BreakpointModelId *id) +{ + QList<int> breakpoints = d->breakpoints.values(*id); + d->breakpoints.remove(*id); + + foreach (int bp, breakpoints) { + QByteArray request; + + JsonInputStream(request) << '{' << INITIALPARAMS ; + JsonInputStream(request) << ',' << "command" << ':' << "clearbreakpoint"; + + JsonInputStream(request) << ',' << "arguments" << ':'; + JsonInputStream(request) << '{' << "breakpoint" << ':' << bp; + JsonInputStream(request) << '}'; + + JsonInputStream(request) << '}'; + + sendMessage(packMessage(request)); + } +} + +void QmlV8DebuggerClient::setBreakpoints() +{ +} + +void QmlV8DebuggerClient::assignValueInDebugger(const QByteArray expr, const quint64 &id, + const QString &property, const QString value) +{ + //TODO:: +} + +void QmlV8DebuggerClient::updateWatchData(const WatchData *data) +{ + if (!data->iname.startsWith("watch.")) + return; + + QByteArray request; + + JsonInputStream(request) << '{' << INITIALPARAMS ; + JsonInputStream(request) << ',' << "command" << ':' << "evaluate"; + + JsonInputStream(request) << ',' << "arguments" << ':'; + JsonInputStream(request) << '{' << "expression" << ':' << data->exp; + JsonInputStream(request) << ',' << "frame" << ':' << d->engine->stackHandler()->currentFrame().level; + JsonInputStream(request) << '}'; + + JsonInputStream(request) << '}'; + + d->watches.insert(d->sequence,data->iname); + + sendMessage(packMessage(request)); + +} + +void QmlV8DebuggerClient::executeDebuggerCommand(const QString &command) +{ + QByteArray request; + + JsonInputStream(request) << '{' << INITIALPARAMS ; + JsonInputStream(request) << ',' << "command" << ':' << "evaluate"; + + JsonInputStream(request) << ',' << "arguments" << ':'; + JsonInputStream(request) << '{' << "expression" << ':' << command; + JsonInputStream(request) << ',' << "global" << ':' << true; + JsonInputStream(request) << '}'; + + JsonInputStream(request) << '}'; + + sendMessage(packMessage(request)); + +} + +void QmlV8DebuggerClient::synchronizeWatchers(const QStringList &watchers) +{ + //TODO:: send watchers list +} + +void QmlV8DebuggerClient::expandObject(const QByteArray &iname, quint64 objectId) +{ + d->locals.insert(objectId,iname); + QList<int> ids; + ids.append(objectId); + + QByteArray request; + + JsonInputStream(request) << '{' << INITIALPARAMS ; + JsonInputStream(request) << ',' << "command" << ':' << "lookup"; + + JsonInputStream(request) << ',' << "arguments" << ':'; + JsonInputStream(request) << '{' << "handles" << ':' << ids; + JsonInputStream(request) << '}'; + + JsonInputStream(request) << '}'; + + sendMessage(packMessage(request)); + +} + +void QmlV8DebuggerClient::sendPing() +{ + QByteArray request; + + JsonInputStream(request) << '{' << INITIALPARAMS ; + JsonInputStream(request) << ',' << "command" << ':' << "ping" << '}'; + + d->ping = d->sequence; + sendMessage(packMessage(request)); +} + +void QmlV8DebuggerClient::backtrace() +{ + QByteArray request; + + JsonInputStream(request) << '{' << INITIALPARAMS ; + JsonInputStream(request) << ',' << "command" << ':' << "backtrace"; + JsonInputStream(request) << '}'; + + sendMessage(packMessage(request)); +} + +void QmlV8DebuggerClient::messageReceived(const QByteArray &data) +{ + QDataStream ds(data); + QByteArray command; + ds >> command; + + if (command == "V8DEBUG") { + QByteArray response; + ds >> response; + + JsonValue value(response); + QString type = value.findChild("type").toVariant().toString(); + + if (type == "response") { + + if (!value.findChild("success").toVariant().toBool()) { + //TODO:: Error + qDebug() << Q_FUNC_INFO << value.toString(true,2); + return; + } + + QString debugCommand(value.findChild("command").toVariant().toString()); + if (debugCommand == "pong") { + //DO NOTHING + } else if (debugCommand == "backtrace") { + setStackFrames(response); + + } else if (debugCommand == "lookup") { + expandLocal(response); + + } else if (debugCommand == "setbreakpoint") { + int sequence = value.findChild("request_seq").toVariant().toInt(); + int breakpoint = value.findChild("body").findChild("breakpoint").toVariant().toInt(); + d->breakpoints.insertMulti(d->breakpointsSync.take(sequence),breakpoint); + + } else if (debugCommand == "evaluate") { + setExpression(response); + + } else { + //TODO:: + //qDebug() << Q_FUNC_INFO << value.toString(true,2); + } + + } else if (type == "event") { + QString event(value.findChild("event").toVariant().toString()); + + if (event == "break") { + d->engine->inferiorSpontaneousStop(); + backtrace(); + } + } + } +} + +void QmlV8DebuggerClient::setStackFrames(QByteArray &message) +{ + d->frames = message; + JsonValue response(message); + + JsonValue refs = response.findChild("refs"); + JsonValue body = response.findChild("body"); + + int totalFrames = body.findChild("totalFrames").toVariant().toInt(); + JsonValue stackFrames = body.findChild("frames"); + + StackFrames ideStackFrames; + for (int i = 0; i != totalFrames; ++i) { + + JsonValue stackFrame = stackFrames.childAt(i); + + StackFrame frame; + + int frameIndex = stackFrame.findChild("index").toVariant().toInt(); + frame.level = frameIndex; + + frame.line = stackFrame.findChild("line").toVariant().toInt() + 1; + + int index = indexInRef(refs,stackFrame.findChild("func").findChild("ref").toVariant().toInt()); + if (index != -1) { + JsonValue func = refs.childAt(index); + frame.function = func.findChild("name").toVariant().toString(); + } + + index = indexInRef(refs,stackFrame.findChild("script").findChild("ref").toVariant().toInt()); + if (index != -1) { + JsonValue script = refs.childAt(index); + frame.file = d->engine->toFileInProject(script.findChild("name").toVariant().toString()); + frame.usable = QFileInfo(frame.file).isReadable(); + } + ideStackFrames << frame; + } + + d->engine->stackHandler()->setFrames(ideStackFrames); + + QString fileName; + QString file; + QString function; + int line = -1; + + if (!ideStackFrames.isEmpty()) { + file = ideStackFrames.at(0).file; + fileName = QFileInfo(file).fileName(); + line = ideStackFrames.at(0).line; + function = ideStackFrames.at(0).function; + } + + BreakHandler *handler = d->engine->breakHandler(); + foreach (BreakpointModelId id, handler->engineBreakpointIds(d->engine)) { + QString processedFilename = QFileInfo(handler->fileName(id)).fileName(); + if (processedFilename == fileName && handler->lineNumber(id) == line) { + QTC_ASSERT(handler->state(id) == BreakpointInserted,/**/); + BreakpointResponse br = handler->response(id); + br.fileName = file; + br.lineNumber = line; + br.functionName = function; + handler->setResponse(id, br); + } + } + + if (!ideStackFrames.isEmpty()) { + setLocals(0); + d->engine->gotoLocation(ideStackFrames.value(0)); + } + +} + +void QmlV8DebuggerClient::setLocals(int frameIndex) +{ + JsonValue response(d->frames); + + JsonValue refs = response.findChild("refs"); + JsonValue body = response.findChild("body"); + + int totalFrames = body.findChild("totalFrames").toVariant().toInt(); + JsonValue stackFrames = body.findChild("frames"); + + + for (int i = 0; i != totalFrames; ++i) { + + JsonValue stackFrame = stackFrames.childAt(i); + int index = stackFrame.findChild("index").toVariant().toInt(); + if (index != frameIndex) + continue; + + JsonValue locals = stackFrame.findChild("locals"); + + d->engine->watchHandler()->beginCycle(); + + int localsCount = locals.childCount(); + for (int j = 0; j != localsCount; ++j) { + JsonValue local = locals.childAt(j); + + WatchData data; + data.exp = local.findChild("name").toVariant().toByteArray(); + data.name = data.exp; + data.iname = "local." + data.exp; + JsonValue val = refs.childAt(indexInRef(refs,local.findChild("value").findChild("ref").toVariant().toInt())); + data.type = val.findChild("type").toVariant().toByteArray(); + + if (data.type == "object") { + data.hasChildren = true; + data.value = val.findChild("className").toVariant().toByteArray(); + + } else if (data.type == "function" || data.type == "undefined") { + data.hasChildren = false; + data.value = val.findChild("text").toVariant().toByteArray(); + + } else { + data.hasChildren = false; + data.value = val.findChild("value").toVariant().toByteArray(); + } + + data.id = val.findChild("handle").toVariant().toInt(); + + d->engine->watchHandler()->insertData(data); + + if (d->engine->watchHandler()->expandedINames().contains(data.iname)) { + expandObject(data.iname, data.id); + } + } + + d->engine->watchHandler()->endCycle(); + } +} + +void QmlV8DebuggerClient::expandLocal(QByteArray &message) +{ + JsonValue response(message); + + JsonValue refs = response.findChild("refs"); + JsonValue body = response.findChild("body"); + JsonValue details = body.childAt(0); + + int id = QString(details.name()).toInt(); + QByteArray prepend = d->locals.take(id); + + JsonValue properties = details.findChild("properties"); + int propertiesCount = properties.childCount(); + + for (int k = 0; k != propertiesCount; ++k) { + JsonValue property = properties.childAt(k); + setPropertyValue(refs,property,prepend); + } +} + +void QmlV8DebuggerClient::setExpression(QByteArray &message) +{ + JsonValue response(message); + JsonValue body = response.findChild("body"); + + int seq = response.findChild("request_seq").toVariant().toInt(); + + if (!d->watches.contains(seq)) { + d->engine->showMessage(body.findChild("text").toVariant().toString(), ScriptConsoleOutput); + return; + } + //TODO:: +// JsonValue refs = response.findChild("refs"); +// JsonValue body = response.findChild("body"); +// JsonValue details = body.childAt(0); + +// int id = QString(details.name()).toInt(); +// QByteArray prepend = d->locals.take(id); + +// JsonValue properties = details.findChild("properties"); +// int propertiesCount = properties.childCount(); + +// for (int k = 0; k != propertiesCount; ++k) { +// JsonValue property = properties.childAt(k); +// setPropertyValue(refs,property,prepend); +// } +} + +void QmlV8DebuggerClient::setPropertyValue(JsonValue &refs, JsonValue &property, QByteArray &prepend) +{ + WatchData data; + data.exp = property.findChild("name").toVariant().toByteArray(); + data.name = data.exp; + data.iname = prepend + '.' + data.exp; + JsonValue val = refs.childAt(indexInRef(refs,property.findChild("ref").toVariant().toInt())); + data.type = val.findChild("type").toVariant().toByteArray(); + + if (data.type == "object") { + data.hasChildren = true; + data.value = val.findChild("className").toVariant().toByteArray(); + + } else if (data.type == "function") { + data.hasChildren = false; + data.value = val.findChild("text").toVariant().toByteArray(); + + } else { + data.hasChildren = false; + data.value = val.findChild("value").toVariant().toByteArray(); + } + + data.id = val.findChild("handle").toVariant().toInt(); + + d->engine->watchHandler()->insertData(data); + + if (d->engine->watchHandler()->expandedINames().contains(data.iname)) { + expandObject(data.iname, data.id); + } +} + +int QmlV8DebuggerClient::indexInRef(const JsonValue &refs, int refIndex) +{ + for (int i = 0; i != refs.childCount(); ++i) { + JsonValue ref = refs.childAt(i); + int index = ref.findChild("handle").toVariant().toInt(); + if (index == refIndex) + return i; + } + return -1; +} + +void QmlV8DebuggerClient::setEngine(QmlEngine *engine) +{ + d->engine = engine; +} + +} // Internal +} // Debugger diff --git a/src/plugins/debugger/qml/qmlv8debuggerclient.h b/src/plugins/debugger/qml/qmlv8debuggerclient.h new file mode 100644 index 0000000000000000000000000000000000000000..870f07b78d98d97664d8828e246f2bd23714c4ed --- /dev/null +++ b/src/plugins/debugger/qml/qmlv8debuggerclient.h @@ -0,0 +1,106 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef QMLV8DEBUGGERCLIENT_H +#define QMLV8DEBUGGERCLIENT_H + +#include "qmldebuggerclient.h" +#include "stackframe.h" +#include "watchdata.h" +#include "qmlengine.h" +#include "json.h" + +namespace Debugger { +namespace Internal { + +class QmlV8DebuggerClientPrivate; + +class QmlV8DebuggerClient : public QmlDebuggerClient +{ + Q_OBJECT + +public: + explicit QmlV8DebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection *client); + ~QmlV8DebuggerClient(); + + void executeStep(); + void executeStepOut(); + void executeNext(); + void executeStepI(); + + void continueInferior(); + void interruptInferior(); + + void activateFrame(int index); + + void insertBreakpoints(BreakHandler *handler, BreakpointModelId *id); + void removeBreakpoints(BreakpointModelId *id); + void setBreakpoints(); + + void assignValueInDebugger(const QByteArray expr, const quint64 &id, + const QString &property, const QString value); + + void updateWatchData(const WatchData *data); + void executeDebuggerCommand(const QString &command); + + void synchronizeWatchers(const QStringList &watchers); + + void expandObject(const QByteArray &iname, quint64 objectId); + void sendPing(); + + void setEngine(QmlEngine *engine); + +signals: + void notifyDebuggerStopped(); + +protected: + void messageReceived(const QByteArray &data); + +private: + void backtrace(); + void setStackFrames(QByteArray &); + void setLocals(int frameIndex); + void setExpression(QByteArray &message); + void expandLocal(QByteArray &message); + void setPropertyValue(Json::JsonValue &refs, Json::JsonValue &property, QByteArray &prepend); + int indexInRef(const Json::JsonValue &refs, int refIndex); + QByteArray packMessage(QByteArray& message); + +private: + QmlV8DebuggerClientPrivate *d; + friend class QmlV8DebuggerClientPrivate; +}; + +} // Internal +} // Debugger + +#endif // QMLV8DEBUGGERCLIENT_H diff --git a/src/plugins/debugger/qml/qscriptdebuggerclient.cpp b/src/plugins/debugger/qml/qscriptdebuggerclient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..51cd2cf46fc2e035e06568fc69e4242eda3b9b02 --- /dev/null +++ b/src/plugins/debugger/qml/qscriptdebuggerclient.cpp @@ -0,0 +1,511 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at info@qt.nokia.com. +** +**************************************************************************/ +#include "qscriptdebuggerclient.h" + +#include "watchdata.h" +#include "watchhandler.h" +#include "breakpoint.h" +#include "breakhandler.h" +#include "debuggerconstants.h" +#include "qmlengine.h" +#include "stackhandler.h" +#include "debuggercore.h" + +#include <QTextDocument> +#include <QFileInfo> +#include <QMessageBox> +#include <extensionsystem/pluginmanager.h> +#include <utils/qtcassert.h> + +namespace Debugger { +namespace Internal { + +struct JSAgentBreakpointData +{ + QByteArray functionName; + QByteArray fileUrl; + qint32 lineNumber; +}; + +struct JSAgentStackData +{ + QByteArray functionName; + QByteArray fileUrl; + qint32 lineNumber; +}; + +uint qHash(const JSAgentBreakpointData &b) +{ + return b.lineNumber ^ qHash(b.fileUrl); +} + +QDataStream &operator<<(QDataStream &s, const JSAgentBreakpointData &data) +{ + return s << data.functionName << data.fileUrl << data.lineNumber; +} + +QDataStream &operator<<(QDataStream &s, const JSAgentStackData &data) +{ + return s << data.functionName << data.fileUrl << data.lineNumber; +} + +QDataStream &operator>>(QDataStream &s, JSAgentBreakpointData &data) +{ + return s >> data.functionName >> data.fileUrl >> data.lineNumber; +} + +QDataStream &operator>>(QDataStream &s, JSAgentStackData &data) +{ + return s >> data.functionName >> data.fileUrl >> data.lineNumber; +} + +bool operator==(const JSAgentBreakpointData &b1, const JSAgentBreakpointData &b2) +{ + return b1.lineNumber == b2.lineNumber && b1.fileUrl == b2.fileUrl; +} + +typedef QSet<JSAgentBreakpointData> JSAgentBreakpoints; +typedef QList<JSAgentStackData> JSAgentStackFrames; + + +static QDataStream &operator>>(QDataStream &s, WatchData &data) +{ + data = WatchData(); + QByteArray name; + QByteArray value; + QByteArray type; + bool hasChildren = false; + s >> data.exp >> name >> value >> type >> hasChildren >> data.id; + data.name = QString::fromUtf8(name); + data.setType(type, false); + data.setValue(QString::fromUtf8(value)); + data.setHasChildren(hasChildren); + data.setAllUnneeded(); + return s; +} + +class QScriptDebuggerClientPrivate +{ +public: + explicit QScriptDebuggerClientPrivate(QScriptDebuggerClient *) : + ping(0), engine(0) + { + + } + + int ping; + QmlEngine *engine; + JSAgentBreakpoints breakpoints; +}; + +QScriptDebuggerClient::QScriptDebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection* client) + : QmlDebuggerClient(client, QLatin1String("JSDebugger")), + d(new QScriptDebuggerClientPrivate(this)) +{ +} + +QScriptDebuggerClient::~QScriptDebuggerClient() +{ + delete d; +} + +void QScriptDebuggerClient::executeStep() +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "STEPINTO"; + rs << cmd; + sendMessage(reply); +} + +void QScriptDebuggerClient::executeStepOut() +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "STEPOUT"; + rs << cmd; + sendMessage(reply); +} + +void QScriptDebuggerClient::executeNext() +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "STEPOVER"; + rs << cmd; + sendMessage(reply); +} + +void QScriptDebuggerClient::executeStepI() +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "STEPINTO"; + rs << cmd; + sendMessage(reply); +} + +void QScriptDebuggerClient::continueInferior() +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "CONTINUE"; + rs << cmd; + sendMessage(reply); +} + +void QScriptDebuggerClient::interruptInferior() +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "INTERRUPT"; + rs << cmd; + sendMessage(reply); +} + +void QScriptDebuggerClient::activateFrame(int index) +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "ACTIVATE_FRAME"; + rs << cmd + << index; + sendMessage(reply); +} + +void QScriptDebuggerClient::insertBreakpoints(BreakHandler *handler, BreakpointModelId *id) +{ + JSAgentBreakpointData bp; + bp.fileUrl = QUrl::fromLocalFile(handler->fileName(*id)).toString().toUtf8(); + bp.lineNumber = handler->lineNumber(*id); + bp.functionName = handler->functionName(*id).toUtf8(); + d->breakpoints.insert(bp); +} + +void QScriptDebuggerClient::removeBreakpoints(BreakpointModelId */*id*/) +{ + +} + +void QScriptDebuggerClient::setBreakpoints() +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "BREAKPOINTS"; + rs << cmd + << d->breakpoints; + + QStringList breakPointsStr; + foreach (const JSAgentBreakpointData &bp, d->breakpoints) { + breakPointsStr << QString("('%1' '%2' %3)").arg(QString(bp.functionName), + QString(bp.fileUrl), QString::number(bp.lineNumber)); + } + + sendMessage(reply); + + d->breakpoints.clear(); +} + +void QScriptDebuggerClient::assignValueInDebugger(const QByteArray expr, const quint64 &id, + const QString &property, const QString value) +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "SET_PROPERTY"; + rs << cmd; + rs << expr << id << property << value; + sendMessage(reply); +} + +void QScriptDebuggerClient::updateWatchData(const WatchData *data) +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "EXEC"; + rs << cmd; + rs << data->iname << data->name; + sendMessage(reply); +} + +void QScriptDebuggerClient::executeDebuggerCommand(const QString &command) +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "EXEC"; + QByteArray console = "console"; + rs << cmd << console << command; + sendMessage(reply); +} + +void QScriptDebuggerClient::synchronizeWatchers(const QStringList &watchers) +{ + // send watchers list + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "WATCH_EXPRESSIONS"; + rs << cmd; + rs << watchers; + sendMessage(reply); +} + +void QScriptDebuggerClient::expandObject(const QByteArray &iname, quint64 objectId) +{ + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "EXPAND"; + rs << cmd; + rs << iname << objectId; + sendMessage(reply); +} + +void QScriptDebuggerClient::sendPing() +{ + d->ping++; + QByteArray reply; + QDataStream rs(&reply, QIODevice::WriteOnly); + QByteArray cmd = "PING"; + rs << cmd; + rs << d->ping; + sendMessage(reply); +} + +void QScriptDebuggerClient::messageReceived(const QByteArray &data) +{ + QByteArray rwData = data; + QDataStream stream(&rwData, QIODevice::ReadOnly); + + QByteArray command; + stream >> command; + + if (command == "STOPPED") { + d->engine->inferiorSpontaneousStop(); + + QString logString = QString::fromLatin1(command); + + JSAgentStackFrames stackFrames; + QList<WatchData> watches; + QList<WatchData> locals; + stream >> stackFrames >> watches >> locals; + + logString += QString::fromLatin1(" (%1 stack frames) (%2 watches) (%3 locals)"). + arg(stackFrames.size()).arg(watches.size()).arg(locals.size()); + + StackFrames ideStackFrames; + for (int i = 0; i != stackFrames.size(); ++i) { + StackFrame frame; + frame.line = stackFrames.at(i).lineNumber; + frame.function = stackFrames.at(i).functionName; + frame.file = d->engine->toFileInProject(QUrl(stackFrames.at(i).fileUrl)); + frame.usable = QFileInfo(frame.file).isReadable(); + frame.level = i + 1; + ideStackFrames << frame; + } + + if (ideStackFrames.size() && ideStackFrames.back().function == "<global>") + ideStackFrames.takeLast(); + d->engine->stackHandler()->setFrames(ideStackFrames); + + d->engine->watchHandler()->beginCycle(); + bool needPing = false; + + foreach (WatchData data, watches) { + data.iname = d->engine->watchHandler()->watcherName(data.exp); + d->engine->watchHandler()->insertData(data); + + if (d->engine->watchHandler()->expandedINames().contains(data.iname)) { + needPing = true; + expandObject(data.iname,data.id); + } + } + + foreach (WatchData data, locals) { + data.iname = "local." + data.exp; + d->engine->watchHandler()->insertData(data); + + if (d->engine->watchHandler()->expandedINames().contains(data.iname)) { + needPing = true; + expandObject(data.iname,data.id); + } + } + + if (needPing) { + sendPing(); + } else { + d->engine->watchHandler()->endCycle(); + } + + bool becauseOfException; + stream >> becauseOfException; + + logString += becauseOfException ? " exception" : " no_exception"; + + if (becauseOfException) { + QString error; + stream >> error; + + logString += QLatin1Char(' '); + logString += error; + d->engine->logMessage(QmlEngine::LogReceive, logString); + + QString msg = stackFrames.isEmpty() + ? tr("<p>An uncaught exception occurred:</p><p>%1</p>") + .arg(Qt::escape(error)) + : tr("<p>An uncaught exception occurred in <i>%1</i>:</p><p>%2</p>") + .arg(stackFrames.value(0).fileUrl, Qt::escape(error)); + showMessageBox(QMessageBox::Information, tr("Uncaught Exception"), msg); + } else { + // + // Make breakpoint non-pending + // + QString file; + QString function; + int line = -1; + + if (!ideStackFrames.isEmpty()) { + file = ideStackFrames.at(0).file; + line = ideStackFrames.at(0).line; + function = ideStackFrames.at(0).function; + } + + BreakHandler *handler = d->engine->breakHandler(); + foreach (BreakpointModelId id, handler->engineBreakpointIds(d->engine)) { + QString processedFilename = handler->fileName(id); + if (processedFilename == file && handler->lineNumber(id) == line) { + QTC_ASSERT(handler->state(id) == BreakpointInserted,/**/); + BreakpointResponse br = handler->response(id); + br.fileName = file; + br.lineNumber = line; + br.functionName = function; + handler->setResponse(id, br); + } + } + + d->engine->logMessage(QmlEngine::LogReceive, logString); + } + + if (!ideStackFrames.isEmpty()) + d->engine->gotoLocation(ideStackFrames.value(0)); + + } else if (command == "RESULT") { + WatchData data; + QByteArray iname; + stream >> iname >> data; + + d->engine->logMessage(QmlEngine::LogReceive, QString("%1 %2 %3").arg(QString(command), + QString(iname), QString(data.value))); + data.iname = iname; + if (iname.startsWith("watch.")) { + d->engine->watchHandler()->insertData(data); + } else if (iname == "console") { + d->engine->showMessage(data.value, ScriptConsoleOutput); + } else { + qWarning() << "QmlEngine: Unexcpected result: " << iname << data.value; + } + } else if (command == "EXPANDED") { + QList<WatchData> result; + QByteArray iname; + stream >> iname >> result; + + d->engine->logMessage(QmlEngine::LogReceive, QString("%1 %2 (%3 x watchdata)").arg( + QString(command), QString(iname), QString::number(result.size()))); + bool needPing = false; + foreach (WatchData data, result) { + data.iname = iname + '.' + data.exp; + d->engine->watchHandler()->insertData(data); + + if (d->engine->watchHandler()->expandedINames().contains(data.iname)) { + needPing = true; + expandObject(data.iname, data.id); + } + } + if (needPing) + sendPing(); + } else if (command == "LOCALS") { + QList<WatchData> locals; + QList<WatchData> watches; + int frameId; + stream >> frameId >> locals; + if (!stream.atEnd()) { // compatibility with jsdebuggeragent from 2.1, 2.2 + stream >> watches; + } + + d->engine->logMessage(QmlEngine::LogReceive, QString("%1 %2 (%3 x locals) (%4 x watchdata)").arg( + QString(command), QString::number(frameId), + QString::number(locals.size()), + QString::number(watches.size()))); + d->engine->watchHandler()->beginCycle(); + bool needPing = false; + foreach (WatchData data, watches) { + data.iname = d->engine->watchHandler()->watcherName(data.exp); + d->engine->watchHandler()->insertData(data); + + if (d->engine->watchHandler()->expandedINames().contains(data.iname)) { + needPing = true; + expandObject(data.iname, data.id); + } + } + + foreach (WatchData data, locals) { + data.iname = "local." + data.exp; + d->engine->watchHandler()->insertData(data); + if (d->engine->watchHandler()->expandedINames().contains(data.iname)) { + needPing = true; + expandObject(data.iname, data.id); + } + } + if (needPing) + sendPing(); + else + d->engine->watchHandler()->endCycle(); + + } else if (command == "PONG") { + int ping; + stream >> ping; + + d->engine->logMessage(QmlEngine::LogReceive, QString("%1 %2").arg(QString(command), QString::number(ping))); + + if (ping == d->ping) + d->engine->watchHandler()->endCycle(); + } else { + qDebug() << Q_FUNC_INFO << "Unknown command: " << command; + d->engine->logMessage(QmlEngine::LogReceive, QString("%1 UNKNOWN COMMAND!!").arg(QString(command))); + } + +} + +void QScriptDebuggerClient::setEngine(QmlEngine *engine) +{ + d->engine = engine; +} + +} // Internal +} // Debugger diff --git a/src/plugins/debugger/qml/qscriptdebuggerclient.h b/src/plugins/debugger/qml/qscriptdebuggerclient.h new file mode 100644 index 0000000000000000000000000000000000000000..d8b693535033290c5785ddc0dc2e4ef7a65bf49a --- /dev/null +++ b/src/plugins/debugger/qml/qscriptdebuggerclient.h @@ -0,0 +1,95 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef QSCRIPTDEBUGGERCLIENT_H +#define QSCRIPTDEBUGGERCLIENT_H + +#include "qmldebuggerclient.h" +#include "stackframe.h" +#include "watchdata.h" +#include "qmlengine.h" + +namespace Debugger { +namespace Internal { + +class QScriptDebuggerClientPrivate; + +class QScriptDebuggerClient : public QmlDebuggerClient +{ + Q_OBJECT + +public: + QScriptDebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection *client); + ~QScriptDebuggerClient(); + + void executeStep(); + void executeStepOut(); + void executeNext(); + void executeStepI(); + + void continueInferior(); + void interruptInferior(); + + void activateFrame(int index); + + void insertBreakpoints(BreakHandler *handler, BreakpointModelId *id); + void removeBreakpoints(BreakpointModelId *id); + void setBreakpoints(); + + void assignValueInDebugger(const QByteArray expr, const quint64 &id, + const QString &property, const QString value); + + void updateWatchData(const WatchData *data); + void executeDebuggerCommand(const QString &command); + + void synchronizeWatchers(const QStringList &watchers); + + void expandObject(const QByteArray &iname, quint64 objectId); + void sendPing(); + + void setEngine(QmlEngine *engine); + +signals: + void notifyDebuggerStopped(); + +protected: + void messageReceived(const QByteArray &data); + +private: + QScriptDebuggerClientPrivate *d; + friend class QScriptDebuggerClientPrivate; +}; + +} // Internal +} // Debugger + +#endif // QSCRIPTDEBUGGERCLIENT_H diff --git a/src/plugins/qt4projectmanager/qt-s60/codaruncontrol.cpp b/src/plugins/qt4projectmanager/qt-s60/codaruncontrol.cpp index fb90db4da65f176ffe0b809f543ad5d290d90edb..39f7ee0a85590fcf937cc8503a9e7a1198fd22a2 100644 --- a/src/plugins/qt4projectmanager/qt-s60/codaruncontrol.cpp +++ b/src/plugins/qt4projectmanager/qt-s60/codaruncontrol.cpp @@ -344,7 +344,7 @@ void CodaRunControl::handleDebugSessionEnded(const CodaCommandResult &result) void CodaRunControl::handleFindProcesses(const CodaCommandResult &result) { - if (result.values.size() && result.values.at(0).type() == JsonValue::Array && result.values.at(0).children().count()) { + if (result.values.size() && result.values.at(0).type() == Json::JsonValue::Array && result.values.at(0).children().count()) { //there are processes running. Cannot run mine appendMessage(tr("The process is already running on the device. Please first close it.\n"), Utils::ErrorMessageFormat); finishRunControl(); @@ -367,7 +367,7 @@ void CodaRunControl::handleCreateProcess(const CodaCommandResult &result) if (ok) { if (m_codaFlags & OptionsUseDebugSession) { if (result.values.size()) { - JsonValue id = result.values.at(0).findChild("ID"); + Json::JsonValue id = result.values.at(0).findChild("ID"); if (id.isValid()) { m_state = StateProcessRunning; m_runningProcessId = id.data(); diff --git a/src/plugins/qt4projectmanager/qt-s60/qt-s60.pri b/src/plugins/qt4projectmanager/qt-s60/qt-s60.pri index 7369cb04a1a3a540a82180edc8171431c6db9e7f..6faede1458cbc7aca48aac26010d10d250d944f7 100644 --- a/src/plugins/qt4projectmanager/qt-s60/qt-s60.pri +++ b/src/plugins/qt4projectmanager/qt-s60/qt-s60.pri @@ -77,3 +77,6 @@ FORMS += $$PWD/s60createpackagestep.ui \ $$PWD/s60publishingbuildsettingspageovi.ui \ $$PWD/s60publishingresultspageovi.ui \ $$PWD/s60publishingsissettingspageovi.ui + +include(../../../shared/json/json.pri) +DEFINES += JSON_INCLUDE_PRI diff --git a/src/shared/symbianutils/json.cpp b/src/shared/json/json.cpp similarity index 97% rename from src/shared/symbianutils/json.cpp rename to src/shared/json/json.cpp index 7ac1c8c2c6a366f9df2d0fefb9e319c3b533ee7c..a73881c927383cf125a6690d9d364b917ad51aa2 100644 --- a/src/shared/symbianutils/json.cpp +++ b/src/shared/json/json.cpp @@ -44,14 +44,13 @@ #include <ctype.h> -//#define DEBUG_JASON #ifdef DEBUG_JASON #define JDEBUG(s) qDebug() << s #else #define JDEBUG(s) #endif -namespace Coda { +using namespace Json; static void skipSpaces(const char *&from, const char *to) { @@ -530,11 +529,23 @@ JsonInputStream &JsonInputStream::operator<<(const QVector<QByteArray> &ba) return *this; } +JsonInputStream &JsonInputStream::operator<<(const QList<int> &in) +{ + m_target.append('['); + const int count = in.size(); + for (int i = 0 ; i < count; i++) { + if (i) + m_target.append(','); + m_target.append(QByteArray::number(in.at(i))); + } + m_target.append(']'); + return *this; +} + JsonInputStream &JsonInputStream::operator<<(bool b) { m_target.append(b ? "true" : "false"); return *this; } -} // namespace Coda diff --git a/src/shared/symbianutils/json.h b/src/shared/json/json.h similarity index 94% rename from src/shared/symbianutils/json.h rename to src/shared/json/json.h index 50e3e15f52ee06bfbe2297a8b144d208a248ffc7..318a7c3d7233d1780c481bd384fe1089dce41495 100644 --- a/src/shared/symbianutils/json.h +++ b/src/shared/json/json.h @@ -30,18 +30,18 @@ ** **************************************************************************/ -#ifndef SYMBIANUTILS_JSON_H -#define SYMBIANUTILS_JSON_H +#ifndef JSON_H +#define JSON_H -#include "symbianutils_global.h" +#include "json_global.h" #include <QtCore/QByteArray> #include <QtCore/QStringList> #include <QtCore/QVector> -namespace Coda { +namespace Json { -class SYMBIANUTILS_EXPORT JsonValue +class JSON_EXPORT JsonValue { public: JsonValue() : m_type(Invalid) {} @@ -106,7 +106,7 @@ private: * Note that strings get double quotes and JSON-escaping, characters should be * used for the array/hash delimiters. * */ -class SYMBIANUTILS_EXPORT JsonInputStream { +class JSON_EXPORT JsonInputStream { public: explicit JsonInputStream(QByteArray &a) : m_target(a) {} @@ -121,6 +121,9 @@ public: // Format as array JsonInputStream &operator<<(const QVector<QByteArray> &ba); + //Format as array + JsonInputStream &operator<<(const QList<int> &in); + JsonInputStream &operator<<(bool b); JsonInputStream &operator<<(int i) @@ -137,6 +140,6 @@ private: QByteArray &m_target; }; -} // namespace Coda +} //namespace Json -#endif // SYMBIANUTILS_JSON_H +#endif // JSON_H diff --git a/src/shared/json/json.pri b/src/shared/json/json.pri new file mode 100644 index 0000000000000000000000000000000000000000..088fc4cb4e41378decdcac1cc8518e3dbd7bc22a --- /dev/null +++ b/src/shared/json/json.pri @@ -0,0 +1,7 @@ +INCLUDEPATH *= $$PWD + +# Input +HEADERS += $$PWD/json_global.h \ + $$PWD/json.h + +SOURCES += $$PWD/json.cpp diff --git a/src/shared/json/json_global.h b/src/shared/json/json_global.h new file mode 100644 index 0000000000000000000000000000000000000000..2d677879a088633a052837f9c739e3c520415514 --- /dev/null +++ b/src/shared/json/json_global.h @@ -0,0 +1,46 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef JSON_GLOBAL_H +#define JSON_GLOBAL_H + +#include <QtCore/qglobal.h> + +#if defined(JSON_BUILD_LIB) +# define JSON_EXPORT Q_DECL_EXPORT +#elif defined(JSON_BUILD_STATIC_LIB) || defined(JSON_INCLUDE_PRI) +# define JSON_EXPORT +#else +# define JSON_EXPORT Q_DECL_IMPORT +#endif + +#endif // SYMBIANUTILS_GLOBAL_H diff --git a/src/shared/symbianutils/codadevice.cpp b/src/shared/symbianutils/codadevice.cpp index c125ac7a26f5d4872f3968e57206e54f678958e2..80a33469ad6c4b4f9e7840c8c241047bc19b8901 100644 --- a/src/shared/symbianutils/codadevice.cpp +++ b/src/shared/symbianutils/codadevice.cpp @@ -112,6 +112,7 @@ static inline QByteArray encodeUsbSerialMessage(const QByteArray &dataIn) return frame; } +using namespace Json; namespace Coda { // ------------- CodaCommandError diff --git a/src/shared/symbianutils/codadevice.h b/src/shared/symbianutils/codadevice.h index 3469268c2ad8101412ce05aa34659f18846f594f..47000087817901c738b694be8ec52ec1fc19487f 100644 --- a/src/shared/symbianutils/codadevice.h +++ b/src/shared/symbianutils/codadevice.h @@ -71,7 +71,7 @@ struct SYMBIANUTILS_EXPORT CodaCommandError { operator bool() const { return isError(); } QString toString() const; void write(QTextStream &str) const; - bool parse(const QVector<JsonValue> &values); + bool parse(const QVector<Json::JsonValue> &values); quint64 timeMS; // Since 1.1.1970 qint64 code; @@ -94,7 +94,7 @@ struct SYMBIANUTILS_EXPORT CodaCommandResult { explicit CodaCommandResult(Type t = SuccessReply); explicit CodaCommandResult(char typeChar, Services service, const QByteArray &request, - const QVector<JsonValue> &values, + const QVector<Json::JsonValue> &values, const QVariant &cookie); QString toString() const; @@ -107,7 +107,7 @@ struct SYMBIANUTILS_EXPORT CodaCommandResult { Services service; QByteArray request; CodaCommandError commandError; - QVector<JsonValue> values; + QVector<Json::JsonValue> values; QVariant cookie; }; @@ -394,7 +394,7 @@ public: static CodaStatResponse parseStat(const CodaCommandResult &r); signals: - void genericCodaEvent(int service, const QByteArray &name, const QVector<JsonValue> &value); + void genericCodaEvent(int service, const QByteArray &name, const QVector<Json::JsonValue> &value); void codaEvent(const Coda::CodaEvent &knownEvent); void unknownEvent(uchar protocolId, const QByteArray& data); void serialPong(const QString &codaVersion); diff --git a/src/shared/symbianutils/codamessage.cpp b/src/shared/symbianutils/codamessage.cpp index 1506e6cfd8ecd33be5c0509520a17dd05169b5b2..9eba08aeba7679822c69a841a1fec4fabe9e69dd 100644 --- a/src/shared/symbianutils/codamessage.cpp +++ b/src/shared/symbianutils/codamessage.cpp @@ -43,6 +43,7 @@ static const char *serviceNamesC[] = "DebugSessionControl", "UnknownService"}; +using namespace Json; namespace Coda { SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep) diff --git a/src/shared/symbianutils/codamessage.h b/src/shared/symbianutils/codamessage.h index f61fdeba4fbb949fcc27546a51f70b8df57897af..b58cd2955f1575bf2071d863d4c9027e087057f7 100644 --- a/src/shared/symbianutils/codamessage.h +++ b/src/shared/symbianutils/codamessage.h @@ -34,6 +34,7 @@ #define CODAMESSAGE_H #include "symbianutils_global.h" +#include "json_global.h" #include <QtCore/QStringList> #include <QtCore/QVector> @@ -42,10 +43,12 @@ QT_BEGIN_NAMESPACE class QTextStream; QT_END_NAMESPACE -namespace Coda { - +namespace Json { class JsonValue; class JsonInputStream; +} + +namespace Coda { enum Services { LocatorService, @@ -101,7 +104,7 @@ struct SYMBIANUTILS_EXPORT RunControlContext { unsigned threadId() const; void clear(); - bool parse(const JsonValue &v); + bool parse(const Json::JsonValue &v); void format(QTextStream &str) const; QString toString() const; @@ -122,7 +125,7 @@ struct SYMBIANUTILS_EXPORT RunControlContext { struct SYMBIANUTILS_EXPORT ModuleLoadEventInfo { ModuleLoadEventInfo(); void clear(); - bool parse(const JsonValue &v); + bool parse(const Json::JsonValue &v); void format(QTextStream &str) const; QByteArray name; @@ -154,7 +157,7 @@ struct SYMBIANUTILS_EXPORT Breakpoint { bool thumb; }; -SYMBIANUTILS_EXPORT JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b); +Json::JsonInputStream &operator<<(Json::JsonInputStream &str, const Breakpoint &b); // Event hierarchy class SYMBIANUTILS_EXPORT CodaEvent @@ -179,7 +182,7 @@ public: Type type() const; virtual QString toString() const; - static CodaEvent *parseEvent(Services s, const QByteArray &name, const QVector<JsonValue> &val); + static CodaEvent *parseEvent(Services s, const QByteArray &name, const QVector<Json::JsonValue> &val); protected: explicit CodaEvent(Type type = None); @@ -252,7 +255,7 @@ public: const RunControlContexts &contexts() const { return m_contexts; } virtual QString toString() const; - static CodaRunControlContextAddedEvent *parseEvent(const QVector<JsonValue> &val); + static CodaRunControlContextAddedEvent *parseEvent(const QVector<Json::JsonValue> &val); private: const RunControlContexts m_contexts; diff --git a/src/shared/symbianutils/symbianutils.pri b/src/shared/symbianutils/symbianutils.pri index 7bb782f514f1f2c6f75e86486ebb51152637941e..6511d4d41a7fdc52672ecd2e6c7b9fc2c53e9a91 100644 --- a/src/shared/symbianutils/symbianutils.pri +++ b/src/shared/symbianutils/symbianutils.pri @@ -10,14 +10,12 @@ HEADERS += $$PWD/symbianutils_global.h \ $$PWD/symbiandevicemanager.h \ $$PWD/codadevice.h \ $$PWD/codamessage.h \ - $$PWD/json.h \ $$PWD/virtualserialdevice.h SOURCES += $$PWD/codautils.cpp \ $$PWD/symbiandevicemanager.cpp \ $$PWD/codadevice.cpp \ $$PWD/codamessage.cpp \ - $$PWD/json.cpp \ $$PWD/virtualserialdevice.cpp DEFINES += HAS_SERIALPORT @@ -25,3 +23,5 @@ win32:SOURCES += $$PWD/virtualserialdevice_win.cpp unix:SOURCES += $$PWD/virtualserialdevice_posix.cpp macx:LIBS += -framework IOKit -framework CoreFoundation +include(../../shared/json/json.pri) +DEFINES += JSON_INCLUDE_PRI diff --git a/tests/auto/debugger/dumpers.pro b/tests/auto/debugger/dumpers.pro index 705a96360d79ab7ee85ab75000040091750233ca..96ba21109f676d599dd214d2819126ba2d741565 100644 --- a/tests/auto/debugger/dumpers.pro +++ b/tests/auto/debugger/dumpers.pro @@ -1,5 +1,7 @@ include(../qttest.pri) include($$IDE_SOURCE_TREE/src/libs/symbianutils/symbianutils.pri) +include($$IDE_SOURCE_TREE/src/shared/json/json.pri) +DEFINES += JSON_INCLUDE_PRI DEBUGGERDIR = $$IDE_SOURCE_TREE/src/plugins/debugger UTILSDIR = $$IDE_SOURCE_TREE/src/libs diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index 9471b230bc2bc7342764b362945a0c66924d63e4..81eaeb8f624a13c7ae04a373dc1b663bb1ba01e3 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -162,7 +162,7 @@ public: void testJson(const char* input) { - QCOMPARE('\n' + QString::fromLatin1(Coda::JsonValue(input).toString(false)), + QCOMPARE('\n' + QString::fromLatin1(Json::JsonValue(input).toString(false)), '\n' + QString(input)); }