diff options
author | Jyri Tahtela <jyri.tahtela@nokia.com> | 2011-05-13 11:30:46 (GMT) |
---|---|---|
committer | Jyri Tahtela <jyri.tahtela@nokia.com> | 2011-05-13 11:30:46 (GMT) |
commit | 50ea2454c0ad9b28be6dd5125c74466f0f96d16a (patch) | |
tree | 610e8b4852bca7d23ec5c4b9937a119eed96a124 /src/declarative/debugger | |
parent | 0f750aec4507fffc06acea46a513c6d374564d6a (diff) | |
parent | a80130b07c5b8ee4b06eabbceb65eb23d4d4602e (diff) | |
download | Qt-50ea2454c0ad9b28be6dd5125c74466f0f96d16a.zip Qt-50ea2454c0ad9b28be6dd5125c74466f0f96d16a.tar.gz Qt-50ea2454c0ad9b28be6dd5125c74466f0f96d16a.tar.bz2 |
Merge remote-tracking branch 'qt/4.8'
Conflicts:
src/s60installs/eabi/QtOpenGLu.def
Diffstat (limited to 'src/declarative/debugger')
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(); |