summaryrefslogtreecommitdiffstats
path: root/src/declarative/debugger
diff options
context:
space:
mode:
authorQt Continuous Integration System <qt-info@nokia.com>2011-05-12 07:15:00 (GMT)
committerQt Continuous Integration System <qt-info@nokia.com>2011-05-12 07:15:00 (GMT)
commit51684a152f380ed86bb45303871c0881f138d6d9 (patch)
tree9afce594ecbf1685863f13f342b1c5608c49c7fa /src/declarative/debugger
parentb80c3b1b04c35ae1fa6d7119b6f7dac06ee6494b (diff)
parent5c830ed2a612b940c377fb4e8a2372655f175707 (diff)
downloadQt-51684a152f380ed86bb45303871c0881f138d6d9.zip
Qt-51684a152f380ed86bb45303871c0881f138d6d9.tar.gz
Qt-51684a152f380ed86bb45303871c0881f138d6d9.tar.bz2
Merge branch 'master' of scm.dev.nokia.troll.no:qt/qt-qml-staging into master-integration
* 'master' of scm.dev.nokia.troll.no:qt/qt-qml-staging: QmlDebug: Fix QmlOstPlugin compilation failure QmlDebug: Fix QmlOstPlugin compilation failure Enable performance monitoring at application startup. Augment documentation QmlDebugger: removing slots in Live Preview QmlDebugger: adding slots to items in Live Preview Fixed license header Fixed compile on Windows Added forgotten file qdeclarativeobserverinterface_p.h Removed some trailing whitespace Introduced a CONFIG option that enables declarative debug services Moved the QML Observer Service and related functionality into Qt QDeclarativeDebugServer: Send hello answer before any service messages Removed some superfluous semicolons
Diffstat (limited to 'src/declarative/debugger')
-rw-r--r--src/declarative/debugger/debugger.pri11
-rw-r--r--src/declarative/debugger/qdeclarativedebugserver.cpp69
-rw-r--r--src/declarative/debugger/qdeclarativedebugserver_p.h3
-rw-r--r--src/declarative/debugger/qdeclarativedebugserverconnection_p.h1
-rw-r--r--src/declarative/debugger/qdeclarativedebugservice.cpp10
-rw-r--r--src/declarative/debugger/qdeclarativedebugservice_p.h2
-rw-r--r--src/declarative/debugger/qdeclarativedebugtrace.cpp9
-rw-r--r--src/declarative/debugger/qdeclarativedebugtrace_p.h1
-rw-r--r--src/declarative/debugger/qdeclarativeobserverinterface_p.h69
-rw-r--r--src/declarative/debugger/qdeclarativeobserverservice.cpp137
-rw-r--r--src/declarative/debugger/qdeclarativeobserverservice_p.h91
-rw-r--r--src/declarative/debugger/qjsdebuggeragent.cpp576
-rw-r--r--src/declarative/debugger/qjsdebuggeragent_p.h204
-rw-r--r--src/declarative/debugger/qjsdebugservice.cpp198
-rw-r--r--src/declarative/debugger/qjsdebugservice_p.h99
-rw-r--r--src/declarative/debugger/qpacketprotocol.cpp49
-rw-r--r--src/declarative/debugger/qpacketprotocol_p.h2
17 files changed, 1512 insertions, 19 deletions
diff --git a/src/declarative/debugger/debugger.pri b/src/declarative/debugger/debugger.pri
index 75287b4..044db3c 100644
--- a/src/declarative/debugger/debugger.pri
+++ b/src/declarative/debugger/debugger.pri
@@ -8,7 +8,10 @@ SOURCES += \
$$PWD/qdeclarativedebug.cpp \
$$PWD/qdeclarativedebugtrace.cpp \
$$PWD/qdeclarativedebughelper.cpp \
- $$PWD/qdeclarativedebugserver.cpp
+ $$PWD/qdeclarativedebugserver.cpp \
+ $$PWD/qdeclarativeobserverservice.cpp \
+ $$PWD/qjsdebuggeragent.cpp \
+ $$PWD/qjsdebugservice.cpp
HEADERS += \
$$PWD/qdeclarativedebuggerstatus_p.h \
@@ -20,4 +23,8 @@ HEADERS += \
$$PWD/qdeclarativedebugtrace_p.h \
$$PWD/qdeclarativedebughelper_p.h \
$$PWD/qdeclarativedebugserver_p.h \
- debugger/qdeclarativedebugserverconnection_p.h
+ $$PWD/qdeclarativedebugserverconnection_p.h \
+ $$PWD/qdeclarativeobserverservice_p.h \
+ $$PWD/qdeclarativeobserverinterface_p.h \
+ $$PWD/qjsdebuggeragent_p.h \
+ $$PWD/qjsdebugservice_p.h
diff --git a/src/declarative/debugger/qdeclarativedebugserver.cpp b/src/declarative/debugger/qdeclarativedebugserver.cpp
index 5112af0..4343870 100644
--- a/src/declarative/debugger/qdeclarativedebugserver.cpp
+++ b/src/declarative/debugger/qdeclarativedebugserver.cpp
@@ -90,7 +90,11 @@ public:
QHash<QString, QDeclarativeDebugService *> plugins;
QStringList clientPlugins;
bool gotHello;
+ QString waitingForMsgFromService;
+private:
+ // private slot
+ void _q_deliverMessage(const QString &serviceName, const QByteArray &message);
static QDeclarativeDebugServerConnection *loadConnectionPlugin(const QString &pluginName);
};
@@ -237,7 +241,6 @@ void QDeclarativeDebugServer::receiveMessage(const QByteArray &message)
QDataStream in(message);
if (!d->gotHello) {
-
QString name;
int op;
in >> name >> op;
@@ -252,6 +255,17 @@ void QDeclarativeDebugServer::receiveMessage(const QByteArray &message)
int version;
in >> version >> d->clientPlugins;
+ // Send the hello answer immediately, since it needs to arrive before
+ // the plugins below start sending messages.
+ QByteArray helloAnswer;
+ {
+ QDataStream out(&helloAnswer, QIODevice::WriteOnly);
+ out << QString(QLatin1String("QDeclarativeDebugClient")) << 0 << protocolVersion << d->plugins.keys();
+ }
+ d->connection->send(helloAnswer);
+
+ d->gotHello = true;
+
QHash<QString, QDeclarativeDebugService*>::Iterator iter = d->plugins.begin();
for (; iter != d->plugins.end(); ++iter) {
QDeclarativeDebugService::Status newStatus = QDeclarativeDebugService::Unavailable;
@@ -261,14 +275,6 @@ void QDeclarativeDebugServer::receiveMessage(const QByteArray &message)
iter.value()->statusChanged(newStatus);
}
- QByteArray helloAnswer;
- {
- QDataStream out(&helloAnswer, QIODevice::WriteOnly);
- out << QString(QLatin1String("QDeclarativeDebugClient")) << 0 << protocolVersion << d->plugins.keys();
- }
- d->connection->send(helloAnswer);
-
- d->gotHello = true;
qWarning("QDeclarativeDebugServer: Connection established");
} else {
@@ -306,17 +312,33 @@ void QDeclarativeDebugServer::receiveMessage(const QByteArray &message)
QByteArray message;
in >> message;
- QHash<QString, QDeclarativeDebugService *>::Iterator iter =
- d->plugins.find(name);
- if (iter == d->plugins.end()) {
- qWarning() << "QDeclarativeDebugServer: Message received for missing plugin" << name;
+ if (d->waitingForMsgFromService == name) {
+ // deliver directly so that it is delivered before waitForMessage is returning.
+ d->_q_deliverMessage(name, message);
+ d->waitingForMsgFromService.clear();
} else {
- (*iter)->messageReceived(message);
+ // deliver message in next event loop run.
+ // Fixes the case that the service does start it's own event loop ...,
+ // but the networking code doesn't deliver any new messages because readyRead
+ // hasn't returned.
+ QMetaObject::invokeMethod(this, "_q_deliverMessage", Qt::QueuedConnection,
+ Q_ARG(QString, name),
+ Q_ARG(QByteArray, message));
}
}
}
}
+void QDeclarativeDebugServerPrivate::_q_deliverMessage(const QString &serviceName, const QByteArray &message)
+{
+ QHash<QString, QDeclarativeDebugService *>::Iterator iter = plugins.find(serviceName);
+ if (iter == plugins.end()) {
+ qWarning() << "QDeclarativeDebugServer: Message received for missing plugin" << serviceName;
+ } else {
+ (*iter)->messageReceived(message);
+ }
+}
+
QList<QDeclarativeDebugService*> QDeclarativeDebugServer::services() const
{
const Q_D(QDeclarativeDebugServer);
@@ -374,4 +396,23 @@ void QDeclarativeDebugServer::sendMessage(QDeclarativeDebugService *service,
d->connection->send(msg);
}
+bool QDeclarativeDebugServer::waitForMessage(QDeclarativeDebugService *service)
+{
+ Q_D(QDeclarativeDebugServer);
+
+ if (!service
+ || !d->plugins.contains(service->name())
+ || !d->waitingForMsgFromService.isEmpty())
+ return false;
+
+ d->waitingForMsgFromService = service->name();
+
+ do {
+ d->connection->waitForMessage();
+ } while (!d->waitingForMsgFromService.isEmpty());
+ return true;
+}
+
QT_END_NAMESPACE
+
+#include "moc_qdeclarativedebugserver_p.cpp"
diff --git a/src/declarative/debugger/qdeclarativedebugserver_p.h b/src/declarative/debugger/qdeclarativedebugserver_p.h
index 68ea4d8..72c664c 100644
--- a/src/declarative/debugger/qdeclarativedebugserver_p.h
+++ b/src/declarative/debugger/qdeclarativedebugserver_p.h
@@ -75,10 +75,13 @@ public:
void sendMessage(QDeclarativeDebugService *service, const QByteArray &message);
void receiveMessage(const QByteArray &message);
+ bool waitForMessage(QDeclarativeDebugService *service);
+
private:
friend class QDeclarativeDebugService;
friend class QDeclarativeDebugServicePrivate;
QDeclarativeDebugServer();
+ Q_PRIVATE_SLOT(d_func(), void _q_deliverMessage(QString, QByteArray))
};
QT_END_NAMESPACE
diff --git a/src/declarative/debugger/qdeclarativedebugserverconnection_p.h b/src/declarative/debugger/qdeclarativedebugserverconnection_p.h
index 0c2bdb4..ca267e0 100644
--- a/src/declarative/debugger/qdeclarativedebugserverconnection_p.h
+++ b/src/declarative/debugger/qdeclarativedebugserverconnection_p.h
@@ -62,6 +62,7 @@ public:
virtual bool isConnected() const = 0;
virtual void send(const QByteArray &message) = 0;
virtual void disconnect() = 0;
+ virtual bool waitForMessage() = 0;
};
Q_DECLARE_INTERFACE(QDeclarativeDebugServerConnection, "com.trolltech.Qt.QDeclarativeDebugServerConnection/1.0")
diff --git a/src/declarative/debugger/qdeclarativedebugservice.cpp b/src/declarative/debugger/qdeclarativedebugservice.cpp
index 1b39f1c..c7e6615 100644
--- a/src/declarative/debugger/qdeclarativedebugservice.cpp
+++ b/src/declarative/debugger/qdeclarativedebugservice.cpp
@@ -209,6 +209,16 @@ void QDeclarativeDebugService::sendMessage(const QByteArray &message)
d->server->sendMessage(this, message);
}
+bool QDeclarativeDebugService::waitForMessage()
+{
+ Q_D(QDeclarativeDebugService);
+
+ if (status() != Enabled)
+ return false;
+
+ return d->server->waitForMessage(this);
+}
+
void QDeclarativeDebugService::statusChanged(Status)
{
}
diff --git a/src/declarative/debugger/qdeclarativedebugservice_p.h b/src/declarative/debugger/qdeclarativedebugservice_p.h
index 5e30350..f3d1919 100644
--- a/src/declarative/debugger/qdeclarativedebugservice_p.h
+++ b/src/declarative/debugger/qdeclarativedebugservice_p.h
@@ -69,6 +69,7 @@ public:
Status status() const;
void sendMessage(const QByteArray &);
+ bool waitForMessage();
static int idForObject(QObject *);
static QObject *objectForId(int);
@@ -84,6 +85,7 @@ protected:
private:
friend class QDeclarativeDebugServer;
+ friend class QDeclarativeDebugServerPrivate;
};
QT_END_NAMESPACE
diff --git a/src/declarative/debugger/qdeclarativedebugtrace.cpp b/src/declarative/debugger/qdeclarativedebugtrace.cpp
index 6f28736..edbbe78 100644
--- a/src/declarative/debugger/qdeclarativedebugtrace.cpp
+++ b/src/declarative/debugger/qdeclarativedebugtrace.cpp
@@ -65,9 +65,14 @@ QByteArray QDeclarativeDebugData::toByteArray() const
QDeclarativeDebugTrace::QDeclarativeDebugTrace()
: QDeclarativeDebugService(QLatin1String("CanvasFrameRate")),
- m_enabled(false), m_deferredSend(true)
+ m_enabled(false), m_deferredSend(true), m_messageReceived(false)
{
m_timer.start();
+ if (status() == Enabled) {
+ // wait for first message indicating whether to trace or not
+ while (!m_messageReceived)
+ waitForMessage();
+ }
}
void QDeclarativeDebugTrace::addEvent(EventType t)
@@ -213,6 +218,8 @@ void QDeclarativeDebugTrace::messageReceived(const QByteArray &message)
stream >> m_enabled;
+ m_messageReceived = true;
+
if (!m_enabled)
sendMessages();
}
diff --git a/src/declarative/debugger/qdeclarativedebugtrace_p.h b/src/declarative/debugger/qdeclarativedebugtrace_p.h
index ae0653e..c74cbe0 100644
--- a/src/declarative/debugger/qdeclarativedebugtrace_p.h
+++ b/src/declarative/debugger/qdeclarativedebugtrace_p.h
@@ -120,6 +120,7 @@ private:
QPerformanceTimer m_timer;
bool m_enabled;
bool m_deferredSend;
+ bool m_messageReceived;
QList<QDeclarativeDebugData> m_data;
};
diff --git a/src/declarative/debugger/qdeclarativeobserverinterface_p.h b/src/declarative/debugger/qdeclarativeobserverinterface_p.h
new file mode 100644
index 0000000..501c132
--- /dev/null
+++ b/src/declarative/debugger/qdeclarativeobserverinterface_p.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVEOBSERVERINTERFACE_H
+#define QDECLARATIVEOBSERVERINTERFACE_H
+
+#include <QtDeclarative/private/qdeclarativeglobal_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class Q_DECLARATIVE_EXPORT QDeclarativeObserverInterface
+{
+public:
+ QDeclarativeObserverInterface() {}
+ virtual ~QDeclarativeObserverInterface() {}
+
+ virtual void activate() = 0;
+ virtual void deactivate() = 0;
+};
+
+Q_DECLARE_INTERFACE(QDeclarativeObserverInterface, "com.trolltech.Qt.QDeclarativeObserverInterface/1.0")
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDECLARATIVEOBSERVERINTERFACE_H
diff --git a/src/declarative/debugger/qdeclarativeobserverservice.cpp b/src/declarative/debugger/qdeclarativeobserverservice.cpp
new file mode 100644
index 0000000..a623c55
--- /dev/null
+++ b/src/declarative/debugger/qdeclarativeobserverservice.cpp
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qdeclarativeobserverservice_p.h"
+#include "private/qdeclarativeobserverinterface_p.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QPluginLoader>
+
+#include <QtDeclarative/QDeclarativeView>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QDeclarativeObserverService, serviceInstance)
+
+QDeclarativeObserverService::QDeclarativeObserverService()
+ : QDeclarativeDebugService(QLatin1String("QDeclarativeObserverMode"))
+ , m_observer(0)
+{
+}
+
+QDeclarativeObserverService *QDeclarativeObserverService::instance()
+{
+ return serviceInstance();
+}
+
+void QDeclarativeObserverService::addView(QDeclarativeView *view)
+{
+ m_views.append(view);
+}
+
+void QDeclarativeObserverService::removeView(QDeclarativeView *view)
+{
+ m_views.removeAll(view);
+}
+
+void QDeclarativeObserverService::sendMessage(const QByteArray &message)
+{
+ if (status() != Enabled)
+ return;
+
+ QDeclarativeDebugService::sendMessage(message);
+}
+
+void QDeclarativeObserverService::statusChanged(Status status)
+{
+ if (m_views.isEmpty())
+ return;
+
+ if (status == Enabled) {
+ if (!m_observer)
+ m_observer = loadObserverPlugin();
+
+ if (!m_observer) {
+ qWarning() << "Error while loading observer plugin";
+ return;
+ }
+
+ m_observer->activate();
+ } else {
+ if (m_observer)
+ m_observer->deactivate();
+ }
+}
+
+void QDeclarativeObserverService::messageReceived(const QByteArray &message)
+{
+ emit gotMessage(message);
+}
+
+QDeclarativeObserverInterface *QDeclarativeObserverService::loadObserverPlugin()
+{
+ QStringList pluginCandidates;
+ const QStringList paths = QCoreApplication::libraryPaths();
+ foreach (const QString &libPath, paths) {
+ const QDir dir(libPath + QLatin1String("/qmltooling"));
+ if (dir.exists())
+ foreach (const QString &pluginPath, dir.entryList(QDir::Files))
+ pluginCandidates << dir.absoluteFilePath(pluginPath);
+ }
+
+ foreach (const QString &pluginPath, pluginCandidates) {
+ QPluginLoader loader(pluginPath);
+ if (!loader.load())
+ continue;
+
+ QDeclarativeObserverInterface *observer =
+ qobject_cast<QDeclarativeObserverInterface*>(loader.instance());
+
+ if (observer)
+ return observer;
+ loader.unload();
+ }
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/debugger/qdeclarativeobserverservice_p.h b/src/declarative/debugger/qdeclarativeobserverservice_p.h
new file mode 100644
index 0000000..36f31dc
--- /dev/null
+++ b/src/declarative/debugger/qdeclarativeobserverservice_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVEOBSERVERSERVICE_H
+#define QDECLARATIVEOBSERVERSERVICE_H
+
+#include "private/qdeclarativedebugservice_p.h"
+#include <private/qdeclarativeglobal_p.h>
+
+#include <QtCore/QList>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QDeclarativeView;
+class QDeclarativeObserverInterface;
+
+class Q_DECLARATIVE_EXPORT QDeclarativeObserverService : public QDeclarativeDebugService
+{
+ Q_OBJECT
+
+public:
+ QDeclarativeObserverService();
+ static QDeclarativeObserverService *instance();
+
+ void addView(QDeclarativeView *);
+ void removeView(QDeclarativeView *);
+ QList<QDeclarativeView*> views() const { return m_views; }
+
+ void sendMessage(const QByteArray &message);
+
+Q_SIGNALS:
+ void gotMessage(const QByteArray &message);
+
+protected:
+ virtual void statusChanged(Status status);
+ virtual void messageReceived(const QByteArray &);
+
+private:
+ static QDeclarativeObserverInterface *loadObserverPlugin();
+
+ QList<QDeclarativeView*> m_views;
+ QDeclarativeObserverInterface *m_observer;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QDECLARATIVEOBSERVERSERVICE_H
diff --git a/src/declarative/debugger/qjsdebuggeragent.cpp b/src/declarative/debugger/qjsdebuggeragent.cpp
new file mode 100644
index 0000000..601c8c8
--- /dev/null
+++ b/src/declarative/debugger/qjsdebuggeragent.cpp
@@ -0,0 +1,576 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qjsdebuggeragent_p.h"
+#include "private/qdeclarativedebughelper_p.h"
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qset.h>
+#include <QtCore/qurl.h>
+#include <QtScript/qscriptcontextinfo.h>
+#include <QtScript/qscriptengine.h>
+#include <QtScript/qscriptvalueiterator.h>
+
+QT_BEGIN_NAMESPACE
+
+class QJSDebuggerAgentPrivate
+{
+public:
+ QJSDebuggerAgentPrivate(QJSDebuggerAgent *q)
+ : q(q), state(NoState)
+ {}
+
+ void continueExec();
+ void recordKnownObjects(const QList<JSAgentWatchData> &);
+ QList<JSAgentWatchData> getLocals(QScriptContext *);
+ void positionChange(qint64 scriptId, int lineNumber, int columnNumber);
+ QScriptEngine *engine() { return q->engine(); }
+ void stopped();
+
+public:
+ QJSDebuggerAgent *q;
+ JSDebuggerState state;
+ int stepDepth;
+ int stepCount;
+
+ QEventLoop loop;
+ QHash<qint64, QString> filenames;
+ JSAgentBreakpoints breakpoints;
+ // breakpoints by filename (without path)
+ QHash<QString, JSAgentBreakpointData> fileNameToBreakpoints;
+ QStringList watchExpressions;
+ QSet<qint64> knownObjectIds;
+};
+
+namespace {
+
+class SetupExecEnv
+{
+public:
+ SetupExecEnv(QJSDebuggerAgentPrivate *a)
+ : agent(a),
+ previousState(a->state),
+ hadException(a->engine()->hasUncaughtException())
+ {
+ agent->state = StoppedState;
+ }
+
+ ~SetupExecEnv()
+ {
+ if (!hadException && agent->engine()->hasUncaughtException())
+ agent->engine()->clearExceptions();
+ agent->state = previousState;
+ }
+
+private:
+ QJSDebuggerAgentPrivate *agent;
+ JSDebuggerState previousState;
+ bool hadException;
+};
+
+} // anonymous namespace
+
+static JSAgentWatchData fromScriptValue(const QString &expression,
+ const QScriptValue &value)
+{
+ static const QString arrayStr = QCoreApplication::translate
+ ("Debugger::JSAgentWatchData", "[Array of length %1]");
+ static const QString undefinedStr = QCoreApplication::translate
+ ("Debugger::JSAgentWatchData", "<undefined>");
+
+ JSAgentWatchData data;
+ data.exp = expression.toUtf8();
+ data.name = data.exp;
+ data.hasChildren = false;
+ data.value = value.toString().toUtf8();
+ data.objectId = value.objectId();
+ if (value.isArray()) {
+ data.type = "Array";
+ data.value = arrayStr.arg(value.property(QLatin1String("length")).toString()).toUtf8();
+ data.hasChildren = true;
+ } else if (value.isBool()) {
+ data.type = "Bool";
+ // data.value = value.toBool() ? "true" : "false";
+ } else if (value.isDate()) {
+ data.type = "Date";
+ data.value = value.toDateTime().toString().toUtf8();
+ } else if (value.isError()) {
+ data.type = "Error";
+ } else if (value.isFunction()) {
+ data.type = "Function";
+ } else if (value.isUndefined()) {
+ data.type = undefinedStr.toUtf8();
+ } else if (value.isNumber()) {
+ data.type = "Number";
+ } else if (value.isRegExp()) {
+ data.type = "RegExp";
+ } else if (value.isString()) {
+ data.type = "String";
+ } else if (value.isVariant()) {
+ data.type = "Variant";
+ } else if (value.isQObject()) {
+ const QObject *obj = value.toQObject();
+ data.type = "Object";
+ data.value += '[';
+ data.value += obj->metaObject()->className();
+ data.value += ']';
+ data.hasChildren = true;
+ } else if (value.isObject()) {
+ data.type = "Object";
+ data.hasChildren = true;
+ data.value = "[Object]";
+ } else if (value.isNull()) {
+ data.type = "<null>";
+ } else {
+ data.type = "<unknown>";
+ }
+ return data;
+}
+
+static QList<JSAgentWatchData> expandObject(const QScriptValue &object)
+{
+ QList<JSAgentWatchData> result;
+ QScriptValueIterator it(object);
+ while (it.hasNext()) {
+ it.next();
+ if (it.flags() & QScriptValue::SkipInEnumeration)
+ continue;
+ if (/*object.isQObject() &&*/ it.value().isFunction()) {
+ // Cosmetics: skip all functions and slot, there are too many of them,
+ // and it is not useful information in the debugger.
+ continue;
+ }
+ JSAgentWatchData data = fromScriptValue(it.name(), it.value());
+ result.append(data);
+ }
+ if (result.isEmpty()) {
+ JSAgentWatchData data;
+ data.name = "<no initialized data>";
+ data.hasChildren = false;
+ data.value = " ";
+ data.objectId = 0;
+ result.append(data);
+ }
+ return result;
+}
+
+static QString fileName(const QString &fileUrl)
+{
+ int lastDelimiterPos = fileUrl.lastIndexOf(QLatin1Char('/'));
+ return fileUrl.mid(lastDelimiterPos, fileUrl.size() - lastDelimiterPos);
+}
+
+void QJSDebuggerAgentPrivate::recordKnownObjects(const QList<JSAgentWatchData>& list)
+{
+ foreach (const JSAgentWatchData &data, list)
+ knownObjectIds << data.objectId;
+}
+
+QList<JSAgentWatchData> QJSDebuggerAgentPrivate::getLocals(QScriptContext *ctx)
+{
+ QList<JSAgentWatchData> locals;
+ if (ctx) {
+ QScriptValue activationObject = ctx->activationObject();
+ QScriptValue thisObject = ctx->thisObject();
+ locals = expandObject(activationObject);
+ if (thisObject.isObject()
+ && thisObject.objectId() != engine()->globalObject().objectId()
+ && QScriptValueIterator(thisObject).hasNext())
+ locals.prepend(fromScriptValue(QLatin1String("this"), thisObject));
+ recordKnownObjects(locals);
+ knownObjectIds << activationObject.objectId();
+ }
+ return locals;
+}
+
+/*!
+ Constructs a new agent for the given \a engine. The agent will
+ report debugging-related events (e.g. step completion) to the given
+ \a backend.
+*/
+QJSDebuggerAgent::QJSDebuggerAgent(QScriptEngine *engine, QObject *parent)
+ : QObject(parent)
+ , QScriptEngineAgent(engine)
+ , d(new QJSDebuggerAgentPrivate(this))
+{
+ QJSDebuggerAgent::engine()->setAgent(this);
+}
+
+QJSDebuggerAgent::QJSDebuggerAgent(QDeclarativeEngine *engine, QObject *parent)
+ : QObject(parent)
+ , QScriptEngineAgent(QDeclarativeDebugHelper::getScriptEngine(engine))
+ , d(new QJSDebuggerAgentPrivate(this))
+{
+ QJSDebuggerAgent::engine()->setAgent(this);
+}
+
+/*!
+ Destroys this QJSDebuggerAgent.
+*/
+QJSDebuggerAgent::~QJSDebuggerAgent()
+{
+ engine()->setAgent(0);
+ delete d;
+}
+
+void QJSDebuggerAgent::setBreakpoints(const JSAgentBreakpoints &breakpoints)
+{
+ d->breakpoints = breakpoints;
+
+ d->fileNameToBreakpoints.clear();
+ foreach (const JSAgentBreakpointData &bp, breakpoints)
+ d->fileNameToBreakpoints.insertMulti(fileName(QString::fromUtf8(bp.fileUrl)), bp);
+}
+
+void QJSDebuggerAgent::setWatchExpressions(const QStringList &watchExpressions)
+{
+ d->watchExpressions = watchExpressions;
+}
+
+void QJSDebuggerAgent::stepOver()
+{
+ d->stepDepth = 0;
+ d->state = SteppingOverState;
+ d->continueExec();
+}
+
+void QJSDebuggerAgent::stepInto()
+{
+ d->stepDepth = 0;
+ d->state = SteppingIntoState;
+ d->continueExec();
+}
+
+void QJSDebuggerAgent::stepOut()
+{
+ d->stepDepth = 0;
+ d->state = SteppingOutState;
+ d->continueExec();
+}
+
+void QJSDebuggerAgent::continueExecution()
+{
+ d->state = NoState;
+ d->continueExec();
+}
+
+JSAgentWatchData QJSDebuggerAgent::executeExpression(const QString &expr)
+{
+ SetupExecEnv execEnv(d);
+
+ JSAgentWatchData data = fromScriptValue(expr, engine()->evaluate(expr));
+ d->knownObjectIds << data.objectId;
+ return data;
+}
+
+QList<JSAgentWatchData> QJSDebuggerAgent::expandObjectById(quint64 objectId)
+{
+ SetupExecEnv execEnv(d);
+
+ QScriptValue v;
+ if (d->knownObjectIds.contains(objectId))
+ v = engine()->objectById(objectId);
+
+ QList<JSAgentWatchData> result = expandObject(v);
+ d->recordKnownObjects(result);
+ return result;
+}
+
+QList<JSAgentWatchData> QJSDebuggerAgent::locals()
+{
+ SetupExecEnv execEnv(d);
+ return d->getLocals(engine()->currentContext());
+}
+
+QList<JSAgentWatchData> QJSDebuggerAgent::localsAtFrame(int frameId)
+{
+ SetupExecEnv execEnv(d);
+
+ int deep = 0;
+ QScriptContext *ctx = engine()->currentContext();
+ while (ctx && deep < frameId) {
+ ctx = ctx->parentContext();
+ deep++;
+ }
+
+ return d->getLocals(ctx);
+}
+
+QList<JSAgentStackData> QJSDebuggerAgent::backtrace()
+{
+ SetupExecEnv execEnv(d);
+
+ QList<JSAgentStackData> backtrace;
+
+ for (QScriptContext *ctx = engine()->currentContext(); ctx; ctx = ctx->parentContext()) {
+ QScriptContextInfo info(ctx);
+
+ JSAgentStackData frame;
+ frame.functionName = info.functionName().toUtf8();
+ if (frame.functionName.isEmpty()) {
+ if (ctx->parentContext()) {
+ switch (info.functionType()) {
+ case QScriptContextInfo::ScriptFunction:
+ frame.functionName = "<anonymous>";
+ break;
+ case QScriptContextInfo::NativeFunction:
+ frame.functionName = "<native>";
+ break;
+ case QScriptContextInfo::QtFunction:
+ case QScriptContextInfo::QtPropertyFunction:
+ frame.functionName = "<native slot>";
+ break;
+ }
+ } else {
+ frame.functionName = "<global>";
+ }
+ }
+ frame.lineNumber = info.lineNumber();
+ // if the line number is unknown, fallback to the function line number
+ if (frame.lineNumber == -1)
+ frame.lineNumber = info.functionStartLineNumber();
+
+ frame.fileUrl = info.fileName().toUtf8();
+ backtrace.append(frame);
+ }
+
+ return backtrace;
+}
+
+QList<JSAgentWatchData> QJSDebuggerAgent::watches()
+{
+ SetupExecEnv execEnv(d);
+
+ QList<JSAgentWatchData> watches;
+ foreach (const QString &expr, d->watchExpressions)
+ watches << fromScriptValue(expr, engine()->evaluate(expr));
+ d->recordKnownObjects(watches);
+ return watches;
+}
+
+void QJSDebuggerAgent::setProperty(qint64 objectId,
+ const QString &property,
+ const QString &value)
+{
+ SetupExecEnv execEnv(d);
+
+ if (d->knownObjectIds.contains(objectId)) {
+ QScriptValue object = engine()->objectById(objectId);
+ if (object.isObject()) {
+ QScriptValue result = engine()->evaluate(value);
+ object.setProperty(property, result);
+ }
+ }
+}
+
+/*!
+ \reimp
+*/
+void QJSDebuggerAgent::scriptLoad(qint64 id, const QString &program,
+ const QString &fileName, int)
+{
+ Q_UNUSED(program);
+ d->filenames.insert(id, fileName);
+}
+
+/*!
+ \reimp
+*/
+void QJSDebuggerAgent::scriptUnload(qint64 id)
+{
+ d->filenames.remove(id);
+}
+
+/*!
+ \reimp
+*/
+void QJSDebuggerAgent::contextPush()
+{
+}
+
+/*!
+ \reimp
+*/
+void QJSDebuggerAgent::contextPop()
+{
+}
+
+/*!
+ \reimp
+*/
+void QJSDebuggerAgent::functionEntry(qint64 scriptId)
+{
+ Q_UNUSED(scriptId);
+ d->stepDepth++;
+}
+
+/*!
+ \reimp
+*/
+void QJSDebuggerAgent::functionExit(qint64 scriptId, const QScriptValue &returnValue)
+{
+ Q_UNUSED(scriptId);
+ Q_UNUSED(returnValue);
+ d->stepDepth--;
+}
+
+/*!
+ \reimp
+*/
+void QJSDebuggerAgent::positionChange(qint64 scriptId, int lineNumber, int columnNumber)
+{
+ d->positionChange(scriptId, lineNumber, columnNumber);
+}
+
+void QJSDebuggerAgentPrivate::positionChange(qint64 scriptId, int lineNumber, int columnNumber)
+{
+ Q_UNUSED(columnNumber);
+
+ if (state == StoppedState)
+ return; //no re-entrency
+
+ // check breakpoints
+ if (!breakpoints.isEmpty()) {
+ QHash<qint64, QString>::const_iterator it = filenames.constFind(scriptId);
+ QScriptContext *ctx = engine()->currentContext();
+ QScriptContextInfo info(ctx);
+ if (it == filenames.constEnd()) {
+ // It is possible that the scripts are loaded before the agent is attached
+ QString filename = info.fileName();
+
+ JSAgentStackData frame;
+ frame.functionName = info.functionName().toUtf8();
+
+ QPair<QString, qint32> key = qMakePair(filename, lineNumber);
+ it = filenames.insert(scriptId, filename);
+ }
+
+ const QString filePath = it.value();
+ JSAgentBreakpoints bps = fileNameToBreakpoints.values(fileName(filePath)).toSet();
+
+ foreach (const JSAgentBreakpointData &bp, bps) {
+ if (bp.lineNumber == lineNumber) {
+ stopped();
+ return;
+ }
+ }
+ }
+
+ switch (state) {
+ case NoState:
+ case StoppedState:
+ // Do nothing
+ break;
+ case SteppingOutState:
+ if (stepDepth >= 0)
+ break;
+ //fallthough
+ case SteppingOverState:
+ if (stepDepth > 0)
+ break;
+ //fallthough
+ case SteppingIntoState:
+ stopped();
+ break;
+ }
+
+}
+
+/*!
+ \reimp
+*/
+void QJSDebuggerAgent::exceptionThrow(qint64 scriptId,
+ const QScriptValue &exception,
+ bool hasHandler)
+{
+ Q_UNUSED(scriptId);
+ Q_UNUSED(exception);
+ Q_UNUSED(hasHandler);
+// qDebug() << Q_FUNC_INFO << exception.toString() << hasHandler;
+#if 0 //sometimes, we get exceptions that we should just ignore.
+ if (!hasHandler && state != StoppedState)
+ stopped(true, exception);
+#endif
+}
+
+/*!
+ \reimp
+*/
+void QJSDebuggerAgent::exceptionCatch(qint64 scriptId, const QScriptValue &exception)
+{
+ Q_UNUSED(scriptId);
+ Q_UNUSED(exception);
+}
+
+bool QJSDebuggerAgent::supportsExtension(Extension extension) const
+{
+ return extension == QScriptEngineAgent::DebuggerInvocationRequest;
+}
+
+QVariant QJSDebuggerAgent::extension(Extension extension, const QVariant &argument)
+{
+ if (extension == QScriptEngineAgent::DebuggerInvocationRequest) {
+ d->stopped();
+ return QVariant();
+ }
+ return QScriptEngineAgent::extension(extension, argument);
+}
+
+void QJSDebuggerAgentPrivate::stopped()
+{
+ bool becauseOfException = false;
+ const QScriptValue &exception = QScriptValue();
+
+ knownObjectIds.clear();
+ state = StoppedState;
+
+ emit q->stopped(becauseOfException, exception.toString());
+
+ loop.exec(QEventLoop::ExcludeUserInputEvents);
+}
+
+void QJSDebuggerAgentPrivate::continueExec()
+{
+ loop.quit();
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/debugger/qjsdebuggeragent_p.h b/src/declarative/debugger/qjsdebuggeragent_p.h
new file mode 100644
index 0000000..ce5a044
--- /dev/null
+++ b/src/declarative/debugger/qjsdebuggeragent_p.h
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QJSDEBUGGERAGENT_P_H
+#define QJSDEBUGGERAGENT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtScript/qscriptengineagent.h>
+#include <QtCore/qset.h>
+
+QT_BEGIN_NAMESPACE
+class QScriptValue;
+class QDeclarativeEngine;
+QT_END_NAMESPACE
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QJSDebuggerAgentPrivate;
+
+enum JSDebuggerState
+{
+ NoState,
+ SteppingIntoState,
+ SteppingOverState,
+ SteppingOutState,
+ StoppedState
+};
+
+struct JSAgentWatchData
+{
+ QByteArray exp;
+ QByteArray name;
+ QByteArray value;
+ QByteArray type;
+ bool hasChildren;
+ quint64 objectId;
+};
+
+inline QDataStream &operator<<(QDataStream &s, const JSAgentWatchData &data)
+{
+ return s << data.exp << data.name << data.value
+ << data.type << data.hasChildren << data.objectId;
+}
+
+struct JSAgentStackData
+{
+ QByteArray functionName;
+ QByteArray fileUrl;
+ qint32 lineNumber;
+};
+
+inline QDataStream &operator<<(QDataStream &s, const JSAgentStackData &data)
+{
+ return s << data.functionName << data.fileUrl << data.lineNumber;
+}
+
+struct JSAgentBreakpointData
+{
+ QByteArray functionName;
+ QByteArray fileUrl;
+ qint32 lineNumber;
+};
+
+typedef QSet<JSAgentBreakpointData> JSAgentBreakpoints;
+
+inline QDataStream &operator<<(QDataStream &s, const JSAgentBreakpointData &data)
+{
+ return s << data.functionName << data.fileUrl << data.lineNumber;
+}
+
+inline QDataStream &operator>>(QDataStream &s, JSAgentBreakpointData &data)
+{
+ return s >> data.functionName >> data.fileUrl >> data.lineNumber;
+}
+
+inline bool operator==(const JSAgentBreakpointData &b1, const JSAgentBreakpointData &b2)
+{
+ return b1.lineNumber == b2.lineNumber && b1.fileUrl == b2.fileUrl;
+}
+
+inline uint qHash(const JSAgentBreakpointData &b)
+{
+ return b.lineNumber ^ qHash(b.fileUrl);
+}
+
+
+class QJSDebuggerAgent : public QObject, public QScriptEngineAgent
+{
+ Q_OBJECT
+
+public:
+ QJSDebuggerAgent(QScriptEngine *engine, QObject *parent = 0);
+ QJSDebuggerAgent(QDeclarativeEngine *engine, QObject *parent = 0);
+ ~QJSDebuggerAgent();
+
+ void setBreakpoints(const JSAgentBreakpoints &);
+ void setWatchExpressions(const QStringList &);
+
+ void stepOver();
+ void stepInto();
+ void stepOut();
+ void continueExecution();
+
+ JSAgentWatchData executeExpression(const QString &expr);
+ QList<JSAgentWatchData> expandObjectById(quint64 objectId);
+ QList<JSAgentWatchData> locals();
+ QList<JSAgentWatchData> localsAtFrame(int frameId);
+ QList<JSAgentStackData> backtrace();
+ QList<JSAgentWatchData> watches();
+ void setProperty(qint64 objectId,
+ const QString &property,
+ const QString &value);
+
+ // reimplemented
+ void scriptLoad(qint64 id, const QString &program,
+ const QString &fileName, int baseLineNumber);
+ void scriptUnload(qint64 id);
+
+ void contextPush();
+ void contextPop();
+
+ void functionEntry(qint64 scriptId);
+ void functionExit(qint64 scriptId,
+ const QScriptValue &returnValue);
+
+ void positionChange(qint64 scriptId,
+ int lineNumber, int columnNumber);
+
+ void exceptionThrow(qint64 scriptId,
+ const QScriptValue &exception,
+ bool hasHandler);
+ void exceptionCatch(qint64 scriptId,
+ const QScriptValue &exception);
+
+ bool supportsExtension(Extension extension) const;
+ QVariant extension(Extension extension,
+ const QVariant &argument = QVariant());
+
+Q_SIGNALS:
+ void stopped(bool becauseOfException,
+ const QString &exception);
+
+private:
+ friend class QJSDebuggerAgentPrivate;
+ QJSDebuggerAgentPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QJSDEBUGGERAGENT_P_H
diff --git a/src/declarative/debugger/qjsdebugservice.cpp b/src/declarative/debugger/qjsdebugservice.cpp
new file mode 100644
index 0000000..f8cd095
--- /dev/null
+++ b/src/declarative/debugger/qjsdebugservice.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qjsdebugservice_p.h"
+#include "private/qjsdebuggeragent_p.h"
+
+#include <QtCore/qdatastream.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qstringlist.h>
+#include <QtDeclarative/qdeclarativeengine.h>
+
+Q_GLOBAL_STATIC(QJSDebugService, serviceInstance)
+
+QJSDebugService::QJSDebugService(QObject *parent)
+ : QDeclarativeDebugService(QLatin1String("JSDebugger"), parent)
+ , m_agent(0)
+{
+}
+
+QJSDebugService::~QJSDebugService()
+{
+ delete m_agent;
+}
+
+QJSDebugService *QJSDebugService::instance()
+{
+ return serviceInstance();
+}
+
+void QJSDebugService::addEngine(QDeclarativeEngine *engine)
+{
+ Q_ASSERT(engine);
+ Q_ASSERT(!m_engines.contains(engine));
+
+ m_engines.append(engine);
+}
+
+void QJSDebugService::removeEngine(QDeclarativeEngine *engine)
+{
+ Q_ASSERT(engine);
+ Q_ASSERT(m_engines.contains(engine));
+
+ m_engines.removeAll(engine);
+}
+
+void QJSDebugService::statusChanged(Status status)
+{
+ if (status == Enabled && !m_engines.isEmpty() && !m_agent) {
+ // Multiple engines are currently unsupported
+ QDeclarativeEngine *engine = m_engines.first();
+ m_agent = new QJSDebuggerAgent(engine, engine);
+
+ connect(m_agent, SIGNAL(stopped(bool,QString)),
+ this, SLOT(executionStopped(bool,QString)));
+
+ } else if (status != Enabled && m_agent) {
+ delete m_agent;
+ m_agent = 0;
+ }
+}
+
+void QJSDebugService::messageReceived(const QByteArray &message)
+{
+ if (!m_agent) {
+ qWarning() << "QJSDebugService::messageReceived: No QJSDebuggerAgent available";
+ return;
+ }
+
+ QDataStream ds(message);
+ QByteArray command;
+ ds >> command;
+ if (command == "BREAKPOINTS") {
+ JSAgentBreakpoints breakpoints;
+ ds >> breakpoints;
+ m_agent->setBreakpoints(breakpoints);
+
+ //qDebug() << "BREAKPOINTS";
+ //foreach (const JSAgentBreakpointData &bp, breakpoints)
+ // qDebug() << "BREAKPOINT: " << bp.fileUrl << bp.lineNumber;
+ } else if (command == "WATCH_EXPRESSIONS") {
+ QStringList watchExpressions;
+ ds >> watchExpressions;
+ m_agent->setWatchExpressions(watchExpressions);
+ } else if (command == "STEPOVER") {
+ m_agent->stepOver();
+ } else if (command == "STEPINTO" || command == "INTERRUPT") {
+ m_agent->stepInto();
+ } else if (command == "STEPOUT") {
+ m_agent->stepOut();
+ } else if (command == "CONTINUE") {
+ m_agent->continueExecution();
+ } else if (command == "EXEC") {
+ QByteArray id;
+ QString expr;
+ ds >> id >> expr;
+
+ JSAgentWatchData data = m_agent->executeExpression(expr);
+
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ rs << QByteArray("RESULT") << id << data;
+ sendMessage(reply);
+ } else if (command == "EXPAND") {
+ QByteArray requestId;
+ quint64 objectId;
+ ds >> requestId >> objectId;
+
+ QList<JSAgentWatchData> result = m_agent->expandObjectById(objectId);
+
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ rs << QByteArray("EXPANDED") << requestId << result;
+ sendMessage(reply);
+ } else if (command == "ACTIVATE_FRAME") {
+ int frameId;
+ ds >> frameId;
+
+ QList<JSAgentWatchData> locals = m_agent->localsAtFrame(frameId);
+
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ rs << QByteArray("LOCALS") << frameId << locals;
+ sendMessage(reply);
+ } else if (command == "SET_PROPERTY") {
+ QByteArray id;
+ qint64 objectId;
+ QString property;
+ QString value;
+ ds >> id >> objectId >> property >> value;
+
+ m_agent->setProperty(objectId, property, value);
+
+ //TODO: feedback
+ } else if (command == "PING") {
+ int ping;
+ ds >> ping;
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ rs << QByteArray("PONG") << ping;
+ sendMessage(reply);
+ } else {
+ qDebug() << Q_FUNC_INFO << "Unknown command" << command;
+ }
+
+ QDeclarativeDebugService::messageReceived(message);
+}
+
+void QJSDebugService::executionStopped(bool becauseOfException,
+ const QString &exception)
+{
+ const QList<JSAgentStackData> backtrace = m_agent->backtrace();
+ const QList<JSAgentWatchData> watches = m_agent->watches();
+ const QList<JSAgentWatchData> locals = m_agent->locals();
+
+ QByteArray reply;
+ QDataStream rs(&reply, QIODevice::WriteOnly);
+ rs << QByteArray("STOPPED") << backtrace << watches << locals
+ << becauseOfException << exception;
+ sendMessage(reply);
+}
diff --git a/src/declarative/debugger/qjsdebugservice_p.h b/src/declarative/debugger/qjsdebugservice_p.h
new file mode 100644
index 0000000..6839ca8
--- /dev/null
+++ b/src/declarative/debugger/qjsdebugservice_p.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QJSDEBUGSERVICE_P_H
+#define QJSDEBUGSERVICE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QPointer>
+
+#include "private/qdeclarativedebugservice_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QDeclarativeEngine;
+class QJSDebuggerAgent;
+
+class QJSDebugService : public QDeclarativeDebugService
+{
+ Q_OBJECT
+
+public:
+ QJSDebugService(QObject *parent = 0);
+ ~QJSDebugService();
+
+ static QJSDebugService *instance();
+
+ void addEngine(QDeclarativeEngine *);
+ void removeEngine(QDeclarativeEngine *);
+
+protected:
+ void statusChanged(Status status);
+ void messageReceived(const QByteArray &);
+
+private Q_SLOTS:
+ void executionStopped(bool becauseOfException,
+ const QString &exception);
+
+private:
+ QList<QDeclarativeEngine *> m_engines;
+ QPointer<QJSDebuggerAgent> m_agent;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QJSDEBUGSERVICE_P_H
diff --git a/src/declarative/debugger/qpacketprotocol.cpp b/src/declarative/debugger/qpacketprotocol.cpp
index 15a14cf..c1034a7 100644
--- a/src/declarative/debugger/qpacketprotocol.cpp
+++ b/src/declarative/debugger/qpacketprotocol.cpp
@@ -42,6 +42,7 @@
#include "private/qpacketprotocol_p.h"
#include <QBuffer>
+#include <QElapsedTimer>
QT_BEGIN_NAMESPACE
@@ -114,7 +115,7 @@ Q_OBJECT
public:
QPacketProtocolPrivate(QPacketProtocol * parent, QIODevice * _dev)
: QObject(parent), inProgressSize(-1), maxPacketSize(MAX_PACKET_SIZE),
- dev(_dev)
+ waitingForPacket(false), dev(_dev)
{
Q_ASSERT(4 == sizeof(qint32));
@@ -125,7 +126,7 @@ public:
QObject::connect(this, SIGNAL(invalidPacket()),
parent, SIGNAL(invalidPacket()));
QObject::connect(dev, SIGNAL(readyRead()),
- this, SLOT(readyToRead()), Qt::QueuedConnection);
+ this, SLOT(readyToRead()));
QObject::connect(dev, SIGNAL(aboutToClose()),
this, SLOT(aboutToClose()));
QObject::connect(dev, SIGNAL(bytesWritten(qint64)),
@@ -200,6 +201,7 @@ public Q_SLOTS:
inProgress.clear();
emit readyRead();
+ waitingForPacket = false;
// Need to get trailing data
readyToRead();
@@ -213,6 +215,7 @@ public:
QByteArray inProgress;
qint32 inProgressSize;
qint32 maxPacketSize;
+ bool waitingForPacket;
QIODevice * dev;
};
@@ -324,6 +327,48 @@ QPacket QPacketProtocol::read()
return rv;
}
+/*
+ Returns the difference between msecs and elapsed. If msecs is -1,
+ however, -1 is returned.
+*/
+static int qt_timeout_value(int msecs, int elapsed)
+{
+ if (msecs == -1)
+ return -1;
+
+ int timeout = msecs - elapsed;
+ return timeout < 0 ? 0 : timeout;
+}
+
+/*!
+ This function locks until a new packet is available for reading and the
+ \l{QIODevice::}{readyRead()} signal has been emitted. The function
+ will timeout after \a msecs milliseconds; the default timeout is
+ 30000 milliseconds.
+
+ The function returns true if the readyRead() signal is emitted and
+ there is new data available for reading; otherwise it returns false
+ (if an error occurred or the operation timed out).
+ */
+
+bool QPacketProtocol::waitForReadyRead(int msecs)
+{
+ if (!d->packets.isEmpty())
+ return true;
+
+ QElapsedTimer stopWatch;
+ stopWatch.start();
+
+ d->waitingForPacket = true;
+ do {
+ if (!d->dev->waitForReadyRead(msecs))
+ return false;
+ if (!d->waitingForPacket)
+ return true;
+ msecs = qt_timeout_value(msecs, stopWatch.elapsed());
+ } while (true);
+}
+
/*!
Return the QIODevice passed to the QPacketProtocol constructor.
*/
diff --git a/src/declarative/debugger/qpacketprotocol_p.h b/src/declarative/debugger/qpacketprotocol_p.h
index accb8ef..22bc3c2 100644
--- a/src/declarative/debugger/qpacketprotocol_p.h
+++ b/src/declarative/debugger/qpacketprotocol_p.h
@@ -75,6 +75,8 @@ public:
qint64 packetsAvailable() const;
QPacket read();
+ bool waitForReadyRead(int msecs = 3000);
+
void clear();
QIODevice * device();