Delegate javascript debugging to Script and V8 debugger clients.

The appropriate client handles the debugging based on the service available at the server side.

Change-Id: I46b66036f700fc7e45e8b38cef7f1ce1445b1122
Reviewed-on: http://codereview.qt.nokia.com/2497
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Kai Koehne <kai.koehne@nokia.com>
This commit is contained in:
Aurindam Jana
2011-07-26 16:22:49 +02:00
parent 999265105b
commit ab574ba88d
26 changed files with 1636 additions and 487 deletions

View File

@@ -88,7 +88,7 @@ public:
Status status() const; Status status() const;
void sendMessage(const QByteArray &); virtual void sendMessage(const QByteArray &);
protected: protected:
virtual void statusChanged(Status); virtual void statusChanged(Status);

View File

@@ -1349,7 +1349,7 @@ void CodaGdbAdapter::handleReadRegisters(const CodaCommandResult &result)
logMessage("ERROR: " + result.errorString(), LogError); logMessage("ERROR: " + result.errorString(), LogError);
return; 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(), logMessage(_("Format error in register message: ") + result.toString(),
LogError); LogError);
return; return;

View File

@@ -1,4 +1,6 @@
include($$PWD/../../../libs/qmljsdebugclient/qmljsdebugclient-lib.pri) include($$PWD/../../../libs/qmljsdebugclient/qmljsdebugclient-lib.pri)
include($$PWD/../../../shared/json/json.pri)
DEFINES += JSON_INCLUDE_PRI
HEADERS += \ HEADERS += \
$$PWD/qmlengine.h \ $$PWD/qmlengine.h \
@@ -6,10 +8,15 @@ HEADERS += \
$$PWD/qmldebuggerclient.h \ $$PWD/qmldebuggerclient.h \
$$PWD/qmljsprivateapi.h \ $$PWD/qmljsprivateapi.h \
$$PWD/qmlcppengine.h \ $$PWD/qmlcppengine.h \
$$PWD/scriptconsole.h $$PWD/scriptconsole.h \
$$PWD/qscriptdebuggerclient.h \
$$PWD/qmlv8debuggerclient.h
SOURCES += \ SOURCES += \
$$PWD/qmlengine.cpp \ $$PWD/qmlengine.cpp \
$$PWD/qmladapter.cpp \ $$PWD/qmladapter.cpp \
$$PWD/qmldebuggerclient.cpp \ $$PWD/qmldebuggerclient.cpp \
$$PWD/qmlcppengine.cpp \ $$PWD/qmlcppengine.cpp \
$$PWD/scriptconsole.cpp $$PWD/scriptconsole.cpp \
$$PWD/qscriptdebuggerclient.cpp \
$$PWD/qmlv8debuggerclient.cpp

View File

@@ -33,10 +33,11 @@
#include "qmladapter.h" #include "qmladapter.h"
#include "debuggerstartparameters.h" #include "debuggerstartparameters.h"
#include "qmldebuggerclient.h" #include "qscriptdebuggerclient.h"
#include "qmlv8debuggerclient.h"
#include "qmljsprivateapi.h" #include "qmljsprivateapi.h"
#include "debuggerengine.h" #include "qmlengine.h"
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -61,13 +62,13 @@ public:
} }
QWeakPointer<DebuggerEngine> m_engine; QWeakPointer<DebuggerEngine> m_engine;
Internal::QmlDebuggerClient *m_qmlClient; QmlDebuggerClient *m_qmlClient;
QTimer m_connectionTimer; QTimer m_connectionTimer;
int m_connectionAttempts; int m_connectionAttempts;
int m_maxConnectionAttempts; int m_maxConnectionAttempts;
QDeclarativeDebugConnection *m_conn; QDeclarativeDebugConnection *m_conn;
QList<QByteArray> sendBuffer; QHash<QString, QmlDebuggerClient*> debugClients;
}; };
} // namespace Internal } // 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) void QmlAdapter::connectionErrorOccurred(QAbstractSocket::SocketError socketError)
{ {
showConnectionStatusMessage(tr("Error: (%1) %2", "%1=error code, %2=error message") showConnectionStatusMessage(tr("Error: (%1) %2", "%1=error code, %2=error message")
@@ -177,8 +168,10 @@ void QmlAdapter::clientStatusChanged(QDeclarativeDebugClient::Status status)
logServiceStatusChange(serviceName, status); logServiceStatusChange(serviceName, status);
if (status == QDeclarativeDebugClient::Enabled) if (status == QDeclarativeDebugClient::Enabled) {
flushSendBuffer(); d->m_qmlClient = d->debugClients.value(serviceName);
d->m_qmlClient->flushSendBuffer();
}
} }
void QmlAdapter::connectionStateChanged() void QmlAdapter::connectionStateChanged()
@@ -202,7 +195,8 @@ void QmlAdapter::connectionStateChanged()
showConnectionStatusMessage(tr("connected.\n")); showConnectionStatusMessage(tr("connected.\n"));
if (!d->m_qmlClient) if (!d->m_qmlClient)
createDebuggerClient(); createDebuggerClients();
//reloadEngines(); //reloadEngines();
emit connected(); emit connected();
break; 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))); this, SLOT(clientStatusChanged(QDeclarativeDebugClient::Status)));
connect(d->m_engine.data(), SIGNAL(sendMessage(QByteArray)),
this, SLOT(sendMessage(QByteArray))); Internal::QmlV8DebuggerClient *client2 = new Internal::QmlV8DebuggerClient(d->m_conn);
connect(d->m_qmlClient, SIGNAL(messageWasReceived(QByteArray)), connect(client2, SIGNAL(newStatus(QDeclarativeDebugClient::Status)),
d->m_engine.data(), SLOT(messageReceived(QByteArray))); 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 //engine->startSuccessful(); // FIXME: AAA: port to new debugger states
} }
@@ -243,13 +244,13 @@ QDeclarativeDebugConnection *QmlAdapter::connection() const
void QmlAdapter::showConnectionStatusMessage(const QString &message) void QmlAdapter::showConnectionStatusMessage(const QString &message)
{ {
if (!d->m_engine.isNull()) 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) void QmlAdapter::showConnectionErrorMessage(const QString &message)
{ {
if (!d->m_engine.isNull()) 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) bool QmlAdapter::disableJsDebugging(bool block)
@@ -271,6 +272,15 @@ bool QmlAdapter::disableJsDebugging(bool block)
return isBlocked; 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, void QmlAdapter::logServiceStatusChange(const QString &service,
QDeclarativeDebugClient::Status newStatus) 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); 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 } // namespace Debugger

View File

@@ -70,6 +70,9 @@ public:
bool disableJsDebugging(bool block); bool disableJsDebugging(bool block);
Internal::QmlDebuggerClient *activeDebuggerClient();
QHash<QString, Internal::QmlDebuggerClient*> debuggerClients();
public slots: public slots:
void logServiceStatusChange(const QString &service, QDeclarativeDebugClient::Status newStatus); void logServiceStatusChange(const QString &service, QDeclarativeDebugClient::Status newStatus);
void logServiceActivity(const QString &service, const QString &logMessage); void logServiceActivity(const QString &service, const QString &logMessage);
@@ -82,7 +85,6 @@ signals:
void serviceConnectionError(const QString serviceName); void serviceConnectionError(const QString serviceName);
private slots: private slots:
void sendMessage(const QByteArray &msg);
void connectionErrorOccurred(QAbstractSocket::SocketError socketError); void connectionErrorOccurred(QAbstractSocket::SocketError socketError);
void clientStatusChanged(QDeclarativeDebugClient::Status status); void clientStatusChanged(QDeclarativeDebugClient::Status status);
void connectionStateChanged(); void connectionStateChanged();
@@ -90,10 +92,9 @@ private slots:
private: private:
void connectToViewer(); void connectToViewer();
void createDebuggerClient(); void createDebuggerClients();
void showConnectionStatusMessage(const QString &message); void showConnectionStatusMessage(const QString &message);
void showConnectionErrorMessage(const QString &message); void showConnectionErrorMessage(const QString &message);
void flushSendBuffer();
private: private:
QScopedPointer<Internal::QmlAdapterPrivate> d; QScopedPointer<Internal::QmlAdapterPrivate> d;

View File

@@ -37,13 +37,21 @@
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
QmlDebuggerClient::QmlDebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection* client) class QmlDebuggerClientPrivate
: QDeclarativeDebugClient(QLatin1String("JSDebugger"), client) {
public:
QList<QByteArray> sendBuffer;
};
QmlDebuggerClient::QmlDebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection* client, QLatin1String clientName)
: QDeclarativeDebugClient(clientName, client),
d(new QmlDebuggerClientPrivate())
{ {
} }
QmlDebuggerClient::~QmlDebuggerClient() QmlDebuggerClient::~QmlDebuggerClient()
{ {
delete d;
} }
void QmlDebuggerClient::statusChanged(Status status) void QmlDebuggerClient::statusChanged(Status status)
@@ -51,9 +59,21 @@ void QmlDebuggerClient::statusChanged(Status status)
emit newStatus(status); emit newStatus(status);
} }
void QmlDebuggerClient::messageReceived(const QByteArray &data) void QmlDebuggerClient::sendMessage(const QByteArray &msg)
{ {
emit messageWasReceived(data); if (status() == Enabled) {
QDeclarativeDebugClient::sendMessage(msg);
} else {
d->sendBuffer.append(msg);
}
}
void QmlDebuggerClient::flushSendBuffer()
{
QTC_ASSERT(status() == QDeclarativeDebugClient::Enabled, return);
foreach (const QByteArray &msg, d->sendBuffer)
QDeclarativeDebugClient::sendMessage(msg);
d->sendBuffer.clear();
} }
} // Internal } // Internal

View File

@@ -30,32 +30,70 @@
** **
**************************************************************************/ **************************************************************************/
#ifndef QMLJSDEBUGGERCLIENT_H #ifndef QMLDEBUGGERCLIENT_H
#define QMLJSDEBUGGERCLIENT_H #define QMLDEBUGGERCLIENT_H
#include "qmljsprivateapi.h" #include "qmljsprivateapi.h"
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
class WatchData;
class BreakHandler;
class BreakpointModelId;
class QmlEngine;
class QmlDebuggerClientPrivate;
class QmlDebuggerClient : public QDeclarativeDebugClient class QmlDebuggerClient : public QDeclarativeDebugClient
{ {
Q_OBJECT Q_OBJECT
public: public:
QmlDebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection *client); QmlDebuggerClient(QmlJsDebugClient::QDeclarativeDebugConnection* client, QLatin1String clientName);
virtual ~QmlDebuggerClient(); 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: signals:
void newStatus(QDeclarativeDebugClient::Status status); void newStatus(QDeclarativeDebugClient::Status status);
void messageWasReceived(const QByteArray &data);
protected: protected:
virtual void statusChanged(Status status); virtual void statusChanged(Status status);
virtual void messageReceived(const QByteArray &data); void sendMessage(const QByteArray &msg);
private:
QmlDebuggerClientPrivate *d;
friend class QmlDebuggerClientPrivate;
}; };
} // Internal } // Internal
} // QmlJSInspector } // Debugger
#endif // QMLJSDEBUGGERCLIENT_H #endif // QMLDEBUGGERCLIENT_H

View File

@@ -89,70 +89,6 @@ using namespace ProjectExplorer;
namespace Debugger { namespace Debugger {
namespace Internal { 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 class QmlEnginePrivate
{ {
public: public:
@@ -160,7 +96,6 @@ public:
private: private:
friend class QmlEngine; friend class QmlEngine;
int m_ping;
QmlAdapter m_adapter; QmlAdapter m_adapter;
ApplicationLauncher m_applicationLauncher; ApplicationLauncher m_applicationLauncher;
Utils::FileInProjectFinder fileFinder; Utils::FileInProjectFinder fileFinder;
@@ -168,7 +103,7 @@ private:
}; };
QmlEnginePrivate::QmlEnginePrivate(QmlEngine *q) QmlEnginePrivate::QmlEnginePrivate(QmlEngine *q)
: m_ping(0), m_adapter(q) : m_adapter(q)
{} {}
@@ -466,8 +401,6 @@ void QmlEngine::shutdownEngine()
void QmlEngine::setupEngine() void QmlEngine::setupEngine()
{ {
d->m_ping = 0;
connect(&d->m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)), connect(&d->m_applicationLauncher, SIGNAL(bringToForegroundRequested(qint64)),
runControl(), SLOT(bringApplicationToForeground(qint64)), runControl(), SLOT(bringApplicationToForeground(qint64)),
Qt::UniqueConnection); Qt::UniqueConnection);
@@ -478,12 +411,8 @@ void QmlEngine::setupEngine()
void QmlEngine::continueInferior() void QmlEngine::continueInferior()
{ {
QTC_ASSERT(state() == InferiorStopOk, qDebug() << state()); QTC_ASSERT(state() == InferiorStopOk, qDebug() << state());
QByteArray reply; logMessage(LogSend, "CONTINUE");
QDataStream rs(&reply, QIODevice::WriteOnly); d->m_adapter.activeDebuggerClient()->continueInferior();
QByteArray cmd = "CONTINUE";
rs << cmd;
logMessage(LogSend, cmd);
sendMessage(reply);
resetLocation(); resetLocation();
notifyInferiorRunRequested(); notifyInferiorRunRequested();
notifyInferiorRunOk(); notifyInferiorRunOk();
@@ -491,23 +420,15 @@ void QmlEngine::continueInferior()
void QmlEngine::interruptInferior() void QmlEngine::interruptInferior()
{ {
QByteArray reply; logMessage(LogSend, "INTERRUPT");
QDataStream rs(&reply, QIODevice::WriteOnly); d->m_adapter.activeDebuggerClient()->interruptInferior();
QByteArray cmd = "INTERRUPT";
rs << cmd;
logMessage(LogSend, cmd);
sendMessage(reply);
notifyInferiorStopOk();
} }
void QmlEngine::executeStep() void QmlEngine::executeStep()
{ {
QByteArray reply; logMessage(LogSend, "STEPINTO");
QDataStream rs(&reply, QIODevice::WriteOnly); d->m_adapter.activeDebuggerClient()->executeStep();
QByteArray cmd = "STEPINTO";
rs << cmd;
logMessage(LogSend, cmd);
sendMessage(reply);
resetLocation(); resetLocation();
notifyInferiorRunRequested(); notifyInferiorRunRequested();
notifyInferiorRunOk(); notifyInferiorRunOk();
@@ -515,12 +436,8 @@ void QmlEngine::executeStep()
void QmlEngine::executeStepI() void QmlEngine::executeStepI()
{ {
QByteArray reply; logMessage(LogSend, "STEPINTO");
QDataStream rs(&reply, QIODevice::WriteOnly); d->m_adapter.activeDebuggerClient()->executeStepI();
QByteArray cmd = "STEPINTO";
rs << cmd;
logMessage(LogSend, cmd);
sendMessage(reply);
resetLocation(); resetLocation();
notifyInferiorRunRequested(); notifyInferiorRunRequested();
notifyInferiorRunOk(); notifyInferiorRunOk();
@@ -528,12 +445,8 @@ void QmlEngine::executeStepI()
void QmlEngine::executeStepOut() void QmlEngine::executeStepOut()
{ {
QByteArray reply; logMessage(LogSend, "STEPOUT");
QDataStream rs(&reply, QIODevice::WriteOnly); d->m_adapter.activeDebuggerClient()->executeStepOut();
QByteArray cmd = "STEPOUT";
rs << cmd;
logMessage(LogSend, cmd);
sendMessage(reply);
resetLocation(); resetLocation();
notifyInferiorRunRequested(); notifyInferiorRunRequested();
notifyInferiorRunOk(); notifyInferiorRunOk();
@@ -541,12 +454,8 @@ void QmlEngine::executeStepOut()
void QmlEngine::executeNext() void QmlEngine::executeNext()
{ {
QByteArray reply; logMessage(LogSend, "STEPOVER");
QDataStream rs(&reply, QIODevice::WriteOnly); d->m_adapter.activeDebuggerClient()->executeNext();
QByteArray cmd = "STEPOVER";
rs << cmd;
logMessage(LogSend, cmd);
sendMessage(reply);
resetLocation(); resetLocation();
notifyInferiorRunRequested(); notifyInferiorRunRequested();
notifyInferiorRunOk(); notifyInferiorRunOk();
@@ -577,13 +486,8 @@ void QmlEngine::executeJumpToLine(const ContextData &data)
void QmlEngine::activateFrame(int index) void QmlEngine::activateFrame(int index)
{ {
QByteArray reply; logMessage(LogSend, QString("%1 %2").arg(QString("ACTIVATE_FRAME"), QString::number(index)));
QDataStream rs(&reply, QIODevice::WriteOnly); d->m_adapter.activeDebuggerClient()->activateFrame(index);
QByteArray cmd = "ACTIVATE_FRAME";
rs << cmd
<< index;
logMessage(LogSend, QString("%1 %2").arg(QString(cmd), QString::number(index)));
sendMessage(reply);
gotoLocation(stackHandler()->frames().value(index)); gotoLocation(stackHandler()->frames().value(index));
} }
@@ -602,40 +506,46 @@ void QmlEngine::attemptBreakpointSynchronization()
handler->setEngine(id, this); handler->setEngine(id, this);
} }
JSAgentBreakpoints breakpoints; QStringList breakPointsStr;
foreach (BreakpointModelId id, handler->engineBreakpointIds(this)) { foreach (BreakpointModelId id, handler->engineBreakpointIds(this)) {
if (handler->state(id) == BreakpointRemoveRequested) { if (handler->state(id) == BreakpointRemoveRequested) {
handler->notifyBreakpointRemoveProceeding(id); 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); handler->notifyBreakpointRemoveOk(id);
} else { } else {
if (handler->state(id) == BreakpointInsertRequested) { if (handler->state(id) == BreakpointInsertRequested) {
handler->notifyBreakpointInsertProceeding(id); handler->notifyBreakpointInsertProceeding(id);
} }
JSAgentBreakpointData bp; if (d->m_adapter.activeDebuggerClient())
bp.fileUrl = QUrl::fromLocalFile(handler->fileName(id)).toString().toUtf8(); d->m_adapter.activeDebuggerClient()->insertBreakpoints(handler,&id);
bp.lineNumber = handler->lineNumber(id); else {
bp.functionName = handler->functionName(id).toUtf8(); foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients()) {
breakpoints.insert(bp); client->insertBreakpoints(handler,&id);
}
}
if (handler->state(id) == BreakpointInsertProceeding) { if (handler->state(id) == BreakpointInsertProceeding) {
handler->notifyBreakpointInsertOk(id); 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; logMessage(LogSend, QString("%1 [%2]").arg(QString("BREAKPOINTS"), breakPointsStr.join(", ")));
QDataStream rs(&reply, QIODevice::WriteOnly);
QByteArray cmd = "BREAKPOINTS";
rs << cmd
<< breakpoints;
QStringList breakPointsStr; if (d->m_adapter.activeDebuggerClient()) {
foreach (const JSAgentBreakpointData &bp, breakpoints) { d->m_adapter.activeDebuggerClient()->setBreakpoints();
breakPointsStr << QString("('%1' '%2' %3)").arg(QString(bp.functionName), }
QString(bp.fileUrl), QString::number(bp.lineNumber)); 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 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) const QString &expression, const QVariant &valueV)
{ {
QRegExp inObject("@([0-9a-fA-F]+)->(.+)"); quint64 objectId = data->id;
if (inObject.exactMatch(expression)) { if (objectId > 0 && !expression.isEmpty()) {
bool ok = false; logMessage(LogSend, QString("%1 %2 %3 %4 %5").arg(
quint64 objectId = inObject.cap(1).toULongLong(&ok, 16); QString("SET_PROPERTY"), QString::number(objectId), QString(expression),
QString property = inObject.cap(2); valueV.toString()));
if (ok && objectId > 0 && !property.isEmpty()) { d->m_adapter.activeDebuggerClient()->assignValueInDebugger(expression.toUtf8(), objectId, expression, valueV.toString());
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);
}
} }
} }
@@ -712,19 +612,14 @@ void QmlEngine::updateWatchData(const WatchData &data,
showStatusMessage(tr("Stopped."), 5000); showStatusMessage(tr("Stopped."), 5000);
if (!data.name.isEmpty() && data.isValueNeeded()) { if (!data.name.isEmpty() && data.isValueNeeded()) {
QByteArray reply; logMessage(LogSend, QString("%1 %2 %3").arg(QString("EXEC"), QString(data.iname),
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),
QString(data.name))); QString(data.name)));
sendMessage(reply); d->m_adapter.activeDebuggerClient()->updateWatchData(&data);
} }
if (!data.name.isEmpty() && data.isChildrenNeeded() if (!data.name.isEmpty() && data.isChildrenNeeded()
&& watchHandler()->isExpandedIName(data.iname)) { && watchHandler()->isExpandedIName(data.iname)) {
expandObject(data.iname, data.id); d->m_adapter.activeDebuggerClient()->expandObject(data.iname, data.id);
} }
synchronizeWatchers(); synchronizeWatchers();
@@ -735,39 +630,17 @@ void QmlEngine::updateWatchData(const WatchData &data,
void QmlEngine::synchronizeWatchers() void QmlEngine::synchronizeWatchers()
{ {
QStringList watchedExpressions = watchHandler()->watchedExpressions();
// send watchers list // 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( logMessage(LogSend, QString("%1 %2").arg(
QString(cmd), watchHandler()->watchedExpressions().join(", "))); QString("WATCH_EXPRESSIONS"), watchedExpressions.join(", ")));
sendMessage(reply); if (d->m_adapter.activeDebuggerClient()) {
} d->m_adapter.activeDebuggerClient()->synchronizeWatchers(watchedExpressions);
}
void QmlEngine::expandObject(const QByteArray &iname, quint64 objectId) else {
{ foreach (QmlDebuggerClient *client, d->m_adapter.debuggerClients())
QByteArray reply; client->synchronizeWatchers(watchedExpressions);
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);
} }
unsigned QmlEngine::debuggerCapabilities() const unsigned QmlEngine::debuggerCapabilities() const
@@ -794,209 +667,12 @@ QString QmlEngine::toFileInProject(const QUrl &fileUrl)
return d->fileFinder.findFile(fileUrl); return d->fileFinder.findFile(fileUrl);
} }
void QmlEngine::messageReceived(const QByteArray &message) void QmlEngine::inferiorSpontaneousStop()
{ {
QByteArray rwData = message; if (state() == InferiorRunOk)
QDataStream stream(&rwData, QIODevice::ReadOnly); notifyInferiorSpontaneousStop();
else
QByteArray command; notifyInferiorStopOk();
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)));
}
} }
void QmlEngine::disconnected() void QmlEngine::disconnected()
@@ -1016,14 +692,9 @@ void QmlEngine::wrongSetupMessageBoxFinished(int result)
void QmlEngine::executeDebuggerCommand(const QString& command) void QmlEngine::executeDebuggerCommand(const QString& command)
{ {
QByteArray reply; logMessage(LogSend, QString("%1 %2 %3").arg(QString("EXEC"), QString("console"),
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),
QString(command))); 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) void QmlEngine::logMessage(LogDirection direction, const QString &message)
{ {
QString msg = "JSDebugger"; QString msg = "QmlDebugger";
if (direction == LogSend) { if (direction == LogSend) {
msg += " sending "; msg += " sending ";
} else { } else {

View File

@@ -49,6 +49,11 @@ class QmlEngine : public DebuggerEngine
Q_OBJECT Q_OBJECT
public: public:
enum LogDirection {
LogSend,
LogReceive
};
QmlEngine(const DebuggerStartParameters &startParameters, QmlEngine(const DebuggerStartParameters &startParameters,
DebuggerEngine *masterEngine); DebuggerEngine *masterEngine);
~QmlEngine(); ~QmlEngine();
@@ -62,9 +67,12 @@ public:
int timeout = -1) const; int timeout = -1) const;
void filterApplicationMessage(const QString &msg, int channel); void filterApplicationMessage(const QString &msg, int channel);
virtual bool acceptsWatchesWhileRunning() const; virtual bool acceptsWatchesWhileRunning() const;
QString toFileInProject(const QUrl &fileUrl);
void inferiorSpontaneousStop();
void logMessage(LogDirection direction, const QString &str);
public slots: public slots:
void messageReceived(const QByteArray &message);
void disconnected(); void disconnected();
private slots: private slots:
@@ -120,7 +128,6 @@ private:
unsigned int debuggerCapabilities() const; unsigned int debuggerCapabilities() const;
signals: signals:
void sendMessage(const QByteArray &msg);
void tooltipRequested(const QPoint &mousePos, void tooltipRequested(const QPoint &mousePos,
TextEditor::ITextEditor *editor, int cursorPos); TextEditor::ITextEditor *editor, int cursorPos);
@@ -135,9 +142,6 @@ private slots:
void synchronizeWatchers(); void synchronizeWatchers();
private: private:
void expandObject(const QByteArray &iname, quint64 objectId);
void sendPing();
void closeConnection(); void closeConnection();
void startApplicationLauncher(); void startApplicationLauncher();
void stopApplicationLauncher(); void stopApplicationLauncher();
@@ -148,13 +152,6 @@ private:
const QString &oldBasePath, const QString &newBasePath) const; const QString &oldBasePath, const QString &newBasePath) const;
QString qmlImportPath() const; QString qmlImportPath() const;
enum LogDirection {
LogSend,
LogReceive
};
void logMessage(LogDirection direction, const QString &str);
QString toFileInProject(const QUrl &fileUrl);
private: private:
friend class QmlCppEngine; friend class QmlCppEngine;
QmlEnginePrivate *d; QmlEnginePrivate *d;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -344,7 +344,7 @@ void CodaRunControl::handleDebugSessionEnded(const CodaCommandResult &result)
void CodaRunControl::handleFindProcesses(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 //there are processes running. Cannot run mine
appendMessage(tr("The process is already running on the device. Please first close it.\n"), Utils::ErrorMessageFormat); appendMessage(tr("The process is already running on the device. Please first close it.\n"), Utils::ErrorMessageFormat);
finishRunControl(); finishRunControl();
@@ -367,7 +367,7 @@ void CodaRunControl::handleCreateProcess(const CodaCommandResult &result)
if (ok) { if (ok) {
if (m_codaFlags & OptionsUseDebugSession) { if (m_codaFlags & OptionsUseDebugSession) {
if (result.values.size()) { if (result.values.size()) {
JsonValue id = result.values.at(0).findChild("ID"); Json::JsonValue id = result.values.at(0).findChild("ID");
if (id.isValid()) { if (id.isValid()) {
m_state = StateProcessRunning; m_state = StateProcessRunning;
m_runningProcessId = id.data(); m_runningProcessId = id.data();

View File

@@ -77,3 +77,6 @@ FORMS += $$PWD/s60createpackagestep.ui \
$$PWD/s60publishingbuildsettingspageovi.ui \ $$PWD/s60publishingbuildsettingspageovi.ui \
$$PWD/s60publishingresultspageovi.ui \ $$PWD/s60publishingresultspageovi.ui \
$$PWD/s60publishingsissettingspageovi.ui $$PWD/s60publishingsissettingspageovi.ui
include(../../../shared/json/json.pri)
DEFINES += JSON_INCLUDE_PRI

View File

@@ -44,14 +44,13 @@
#include <ctype.h> #include <ctype.h>
//#define DEBUG_JASON
#ifdef DEBUG_JASON #ifdef DEBUG_JASON
#define JDEBUG(s) qDebug() << s #define JDEBUG(s) qDebug() << s
#else #else
#define JDEBUG(s) #define JDEBUG(s)
#endif #endif
namespace Coda { using namespace Json;
static void skipSpaces(const char *&from, const char *to) static void skipSpaces(const char *&from, const char *to)
{ {
@@ -530,11 +529,23 @@ JsonInputStream &JsonInputStream::operator<<(const QVector<QByteArray> &ba)
return *this; 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) JsonInputStream &JsonInputStream::operator<<(bool b)
{ {
m_target.append(b ? "true" : "false"); m_target.append(b ? "true" : "false");
return *this; return *this;
} }
} // namespace Coda

View File

@@ -30,18 +30,18 @@
** **
**************************************************************************/ **************************************************************************/
#ifndef SYMBIANUTILS_JSON_H #ifndef JSON_H
#define SYMBIANUTILS_JSON_H #define JSON_H
#include "symbianutils_global.h" #include "json_global.h"
#include <QtCore/QByteArray> #include <QtCore/QByteArray>
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <QtCore/QVector> #include <QtCore/QVector>
namespace Coda { namespace Json {
class SYMBIANUTILS_EXPORT JsonValue class JSON_EXPORT JsonValue
{ {
public: public:
JsonValue() : m_type(Invalid) {} JsonValue() : m_type(Invalid) {}
@@ -106,7 +106,7 @@ private:
* Note that strings get double quotes and JSON-escaping, characters should be * Note that strings get double quotes and JSON-escaping, characters should be
* used for the array/hash delimiters. * used for the array/hash delimiters.
* */ * */
class SYMBIANUTILS_EXPORT JsonInputStream { class JSON_EXPORT JsonInputStream {
public: public:
explicit JsonInputStream(QByteArray &a) : m_target(a) {} explicit JsonInputStream(QByteArray &a) : m_target(a) {}
@@ -121,6 +121,9 @@ public:
// Format as array // Format as array
JsonInputStream &operator<<(const QVector<QByteArray> &ba); JsonInputStream &operator<<(const QVector<QByteArray> &ba);
//Format as array
JsonInputStream &operator<<(const QList<int> &in);
JsonInputStream &operator<<(bool b); JsonInputStream &operator<<(bool b);
JsonInputStream &operator<<(int i) JsonInputStream &operator<<(int i)
@@ -137,6 +140,6 @@ private:
QByteArray &m_target; QByteArray &m_target;
}; };
} // namespace Coda } //namespace Json
#endif // SYMBIANUTILS_JSON_H #endif // JSON_H

7
src/shared/json/json.pri Normal file
View File

@@ -0,0 +1,7 @@
INCLUDEPATH *= $$PWD
# Input
HEADERS += $$PWD/json_global.h \
$$PWD/json.h
SOURCES += $$PWD/json.cpp

View File

@@ -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

View File

@@ -112,6 +112,7 @@ static inline QByteArray encodeUsbSerialMessage(const QByteArray &dataIn)
return frame; return frame;
} }
using namespace Json;
namespace Coda { namespace Coda {
// ------------- CodaCommandError // ------------- CodaCommandError

View File

@@ -71,7 +71,7 @@ struct SYMBIANUTILS_EXPORT CodaCommandError {
operator bool() const { return isError(); } operator bool() const { return isError(); }
QString toString() const; QString toString() const;
void write(QTextStream &str) 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 quint64 timeMS; // Since 1.1.1970
qint64 code; qint64 code;
@@ -94,7 +94,7 @@ struct SYMBIANUTILS_EXPORT CodaCommandResult {
explicit CodaCommandResult(Type t = SuccessReply); explicit CodaCommandResult(Type t = SuccessReply);
explicit CodaCommandResult(char typeChar, Services service, explicit CodaCommandResult(char typeChar, Services service,
const QByteArray &request, const QByteArray &request,
const QVector<JsonValue> &values, const QVector<Json::JsonValue> &values,
const QVariant &cookie); const QVariant &cookie);
QString toString() const; QString toString() const;
@@ -107,7 +107,7 @@ struct SYMBIANUTILS_EXPORT CodaCommandResult {
Services service; Services service;
QByteArray request; QByteArray request;
CodaCommandError commandError; CodaCommandError commandError;
QVector<JsonValue> values; QVector<Json::JsonValue> values;
QVariant cookie; QVariant cookie;
}; };
@@ -394,7 +394,7 @@ public:
static CodaStatResponse parseStat(const CodaCommandResult &r); static CodaStatResponse parseStat(const CodaCommandResult &r);
signals: 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 codaEvent(const Coda::CodaEvent &knownEvent);
void unknownEvent(uchar protocolId, const QByteArray& data); void unknownEvent(uchar protocolId, const QByteArray& data);
void serialPong(const QString &codaVersion); void serialPong(const QString &codaVersion);

View File

@@ -43,6 +43,7 @@ static const char *serviceNamesC[] =
"DebugSessionControl", "DebugSessionControl",
"UnknownService"}; "UnknownService"};
using namespace Json;
namespace Coda { namespace Coda {
SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep) SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep)

View File

@@ -34,6 +34,7 @@
#define CODAMESSAGE_H #define CODAMESSAGE_H
#include "symbianutils_global.h" #include "symbianutils_global.h"
#include "json_global.h"
#include <QtCore/QStringList> #include <QtCore/QStringList>
#include <QtCore/QVector> #include <QtCore/QVector>
@@ -42,10 +43,12 @@ QT_BEGIN_NAMESPACE
class QTextStream; class QTextStream;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace Coda { namespace Json {
class JsonValue; class JsonValue;
class JsonInputStream; class JsonInputStream;
}
namespace Coda {
enum Services { enum Services {
LocatorService, LocatorService,
@@ -101,7 +104,7 @@ struct SYMBIANUTILS_EXPORT RunControlContext {
unsigned threadId() const; unsigned threadId() const;
void clear(); void clear();
bool parse(const JsonValue &v); bool parse(const Json::JsonValue &v);
void format(QTextStream &str) const; void format(QTextStream &str) const;
QString toString() const; QString toString() const;
@@ -122,7 +125,7 @@ struct SYMBIANUTILS_EXPORT RunControlContext {
struct SYMBIANUTILS_EXPORT ModuleLoadEventInfo { struct SYMBIANUTILS_EXPORT ModuleLoadEventInfo {
ModuleLoadEventInfo(); ModuleLoadEventInfo();
void clear(); void clear();
bool parse(const JsonValue &v); bool parse(const Json::JsonValue &v);
void format(QTextStream &str) const; void format(QTextStream &str) const;
QByteArray name; QByteArray name;
@@ -154,7 +157,7 @@ struct SYMBIANUTILS_EXPORT Breakpoint {
bool thumb; bool thumb;
}; };
SYMBIANUTILS_EXPORT JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b); Json::JsonInputStream &operator<<(Json::JsonInputStream &str, const Breakpoint &b);
// Event hierarchy // Event hierarchy
class SYMBIANUTILS_EXPORT CodaEvent class SYMBIANUTILS_EXPORT CodaEvent
@@ -179,7 +182,7 @@ public:
Type type() const; Type type() const;
virtual QString toString() 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: protected:
explicit CodaEvent(Type type = None); explicit CodaEvent(Type type = None);
@@ -252,7 +255,7 @@ public:
const RunControlContexts &contexts() const { return m_contexts; } const RunControlContexts &contexts() const { return m_contexts; }
virtual QString toString() const; virtual QString toString() const;
static CodaRunControlContextAddedEvent *parseEvent(const QVector<JsonValue> &val); static CodaRunControlContextAddedEvent *parseEvent(const QVector<Json::JsonValue> &val);
private: private:
const RunControlContexts m_contexts; const RunControlContexts m_contexts;

View File

@@ -10,14 +10,12 @@ HEADERS += $$PWD/symbianutils_global.h \
$$PWD/symbiandevicemanager.h \ $$PWD/symbiandevicemanager.h \
$$PWD/codadevice.h \ $$PWD/codadevice.h \
$$PWD/codamessage.h \ $$PWD/codamessage.h \
$$PWD/json.h \
$$PWD/virtualserialdevice.h $$PWD/virtualserialdevice.h
SOURCES += $$PWD/codautils.cpp \ SOURCES += $$PWD/codautils.cpp \
$$PWD/symbiandevicemanager.cpp \ $$PWD/symbiandevicemanager.cpp \
$$PWD/codadevice.cpp \ $$PWD/codadevice.cpp \
$$PWD/codamessage.cpp \ $$PWD/codamessage.cpp \
$$PWD/json.cpp \
$$PWD/virtualserialdevice.cpp $$PWD/virtualserialdevice.cpp
DEFINES += HAS_SERIALPORT DEFINES += HAS_SERIALPORT
@@ -25,3 +23,5 @@ win32:SOURCES += $$PWD/virtualserialdevice_win.cpp
unix:SOURCES += $$PWD/virtualserialdevice_posix.cpp unix:SOURCES += $$PWD/virtualserialdevice_posix.cpp
macx:LIBS += -framework IOKit -framework CoreFoundation macx:LIBS += -framework IOKit -framework CoreFoundation
include(../../shared/json/json.pri)
DEFINES += JSON_INCLUDE_PRI

View File

@@ -1,5 +1,7 @@
include(../qttest.pri) include(../qttest.pri)
include($$IDE_SOURCE_TREE/src/libs/symbianutils/symbianutils.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 DEBUGGERDIR = $$IDE_SOURCE_TREE/src/plugins/debugger
UTILSDIR = $$IDE_SOURCE_TREE/src/libs UTILSDIR = $$IDE_SOURCE_TREE/src/libs

View File

@@ -162,7 +162,7 @@ public:
void testJson(const char* input) 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)); '\n' + QString(input));
} }