From bfa609e33650061ef656a0c998d6a070fe0128c6 Mon Sep 17 00:00:00 2001 From: Olivier Goffart <olivier.goffart@nokia.com> Date: Thu, 22 Jul 2010 13:00:26 +0200 Subject: [PATCH] QML JS Debugger: Better way to stream watch data QVariant does not work as they can contain custom type that corrypt the stream --- src/plugins/debugger/qml/qmlengine.cpp | 111 ++++-------------- src/plugins/debugger/qml/qmlengine.h | 1 - src/tools/qml/qmlobserver/jsdebuggeragent.cpp | 88 +++++++++++++- 3 files changed, 105 insertions(+), 95 deletions(-) diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index c00d308fa98..27163ef3fae 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -81,6 +81,19 @@ namespace Debugger { namespace Internal { +QDataStream& operator>>(QDataStream& s, WatchData &data) +{ + data = WatchData(); + QString value; + QString type; + bool hasChildren; + s >> data.exp >> data.name >> value >> type >> hasChildren; + data.setType(type, false); + data.setValue(value); + data.setHasChildren(hasChildren); + return s; +} + class QmlResponse { public: @@ -530,74 +543,6 @@ void QmlEngine::updateWatchData(const WatchData &data) } } -void QmlEngine::updateSubItem(WatchData &data, const QVariant &value) -{ - QList<WatchData> children; - switch (value.userType()) { - case QVariant::Invalid:{ - const QString nullValue = QLatin1String("<undefined>"); - data.setType(nullValue, false); - data.setValue(nullValue); - break;} - case QVariant::Map: { - data.setType(QString::fromLatin1("Object"), false); - QVariantMap map = value.toMap(); - for (QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it) { - WatchData childData; - childData.iname = data.iname + '.' + it.key().toLatin1(); - childData.exp = it.key().toLatin1(); - childData.name = it.key(); - updateSubItem(childData, it.value()); - children.append(childData); - } - break; - } - case QVariant::List: { - data.setType(QString::fromLatin1("Array"), false); - QVariantList list = value.toList(); - QStringList values; - for (int i = 0; i < list.count(); ++i) { - WatchData childData; - childData.exp = QByteArray::number(i); - childData.iname = data.iname + '.' + childData.exp; - childData.name = QString::number(i); - updateSubItem(childData, list.at(i)); - children.append(childData); - values.append(list.at(i).toString()); - } - data.setValue(QLatin1Char('[') + values.join(QLatin1String(",")) + QLatin1Char(']')); - break; - } - case QVariant::Bool: - data.setType(QLatin1String("Bool"), false); - data.setValue(value.toBool() ? QLatin1String("true") : QLatin1String("false")); - data.setHasChildren(false); - break; - case QVariant::Date: - case QVariant::DateTime: - case QVariant::Time: - data.setType(QLatin1String("Date"), false); - data.setValue(value.toDateTime().toString()); - data.setHasChildren(false); - break; - case QVariant::UInt: - case QVariant::Int: - case QVariant::Double: - data.setType(QLatin1String("Number"), false); - data.setValue(QString::number(value.toDouble())); - break; - case QVariant::String: - data.setType(QLatin1String("String"), false); - data.setValue(value.toString()); - break; - default: - data.setType(QString::fromLatin1(value.typeName()), false); - data.setValue(value.toString()); - } - data.setHasChildren(!children.isEmpty()); - watchHandler()->insertData(data); -} - DebuggerEngine *createQmlEngine(const DebuggerStartParameters &sp) { return new QmlEngine(sp); @@ -632,8 +577,8 @@ void QmlEngine::messageReceived(const QByteArray &message) notifyInferiorSpontaneousStop(); QList<QPair<QString, QPair<QString, qint32> > > backtrace; - QList<QPair<QString, QVariant> > watches; - QVariant locals; + QList<WatchData> watches; + QList<WatchData> locals; stream >> backtrace >> watches >> locals; StackFrames stackFrames; @@ -651,33 +596,23 @@ void QmlEngine::messageReceived(const QByteArray &message) watchHandler()->beginCycle(); - typedef QPair<QString, QVariant > Iterator2; - foreach (const Iterator2 &it, watches) { - WatchData data; - data.name = it.first; - data.exp = it.first.toUtf8(); + foreach (WatchData data, watches) { data.iname = watchHandler()->watcherName(data.exp); - updateSubItem(data, it.second); + watchHandler()->insertData(data); } -// QVariantMap localsMap = locals.toMap(); -// for (QVariantMap::const_iterator it = localsMap.constBegin(); it != localsMap.constEnd(); it++) - { - WatchData localData; - localData.iname = "local"; - localData.name = QLatin1String("local"); - updateSubItem(localData, locals); + foreach (WatchData data, locals) { + data.iname = "local." + data.exp; + watchHandler()->insertData(data); } watchHandler()->endCycle(); } else if (command == "RESULT") { WatchData data; - QVariant variant; - stream >> data.iname >> data.name >> variant; - data.exp = data.name.toUtf8(); - updateSubItem(data, variant); - qDebug() << Q_FUNC_INFO << this << data.name << data.iname << variant; + QByteArray iname; + stream >> iname >> data; + data.iname = iname; watchHandler()->insertData(data); } else { qDebug() << Q_FUNC_INFO << "Unknown command: " << command; diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h index ba6213156a4..dc4d9010af7 100644 --- a/src/plugins/debugger/qml/qmlengine.h +++ b/src/plugins/debugger/qml/qmlengine.h @@ -120,7 +120,6 @@ private: void maybeBreakNow(bool byFunction); void updateWatchData(const WatchData &data); void updateLocals(); - void updateSubItem(WatchData& data, const QVariant& value); unsigned int debuggerCapabilities() const; diff --git a/src/tools/qml/qmlobserver/jsdebuggeragent.cpp b/src/tools/qml/qmlobserver/jsdebuggeragent.cpp index c54c721f7b8..d79b18c4d4e 100644 --- a/src/tools/qml/qmlobserver/jsdebuggeragent.cpp +++ b/src/tools/qml/qmlobserver/jsdebuggeragent.cpp @@ -47,9 +47,78 @@ #include <QtScript/QScriptContextInfo> #include <QtCore/QDebug> #include <QtCore/QUrl> +#include <QtCore/QDateTime> +#include <QtScript/qscriptvalueiterator.h> QT_BEGIN_NAMESPACE +namespace { +struct JSAgentWatchData { + QByteArray exp; + QString name; + QString value; + QString type; + bool hasChildren; + + static JSAgentWatchData fromScriptValue(const QString &expression, const QScriptValue &value) + { + JSAgentWatchData data; + data.exp = expression.toUtf8(); + data.name = expression; + data.hasChildren = false; + data.value = value.toString(); + if (value.isArray()) { + data.type = QLatin1String("Array"); + data.value = QString::fromLatin1("[Array of length %1]").arg(value.property("length").toString()); + data.hasChildren = true; + } else if (value.isBool()) { + data.type = QLatin1String("Bool"); +// data.value = value.toBool() ? QLatin1String("true") : QLatin1String("false"); + } else if (value.isDate()) { + data.type = QLatin1String("Date"); + data.value = value.toDateTime().toString(); + } else if (value.isError()) { + data.type = QLatin1String("Error"); + } else if (value.isFunction()) { + data.type = QLatin1String("Function"); + } else if (value.isUndefined()) { + data.type = QLatin1String("<undefined>"); + } else if (value.isNumber()) { + data.type = QLatin1String("Number"); + } else if (value.isRegExp()) { + data.type = QLatin1String("RegExp"); + } else if (value.isString()) { + data.type = QLatin1String("String"); + } else if (value.isVariant()) { + data.type = QLatin1String("Variant"); + } else if (value.isObject()) { + data.type = QLatin1String("Object"); + data.hasChildren = true; + data.value = QLatin1String("[Object]"); +/* } else if (value.isQMetaObject()) { + data.setType(QLatin1String("QMetaObject"), false); + data.setValue(QString(QLatin1Char(' '))); + } else if (value.isQObject()) { + data.type = QLatin1String("QObject"); + data.hasChildren = true;*/ + } else if (value.isNull()) { + data.type = QLatin1String("<null>"); + } else { + data.type = QLatin1String("<unknown>"); + } + return data; + } +}; + + +QDataStream& operator<<(QDataStream& s, const JSAgentWatchData& data) +{ + return s << data.exp << data.name << data.value << data.type << data.hasChildren; +} + +} + + /*! Constructs a new agent for the given \a engine. The agent will report debugging-related events (e.g. step completion) to the given @@ -219,13 +288,13 @@ void JSDebuggerAgent::messageReceived(const QByteArray& message) QString expr; ds >> id >> expr; - QVariant val = engine()->evaluate(expr).toVariant(); + JSAgentWatchData data = JSAgentWatchData::fromScriptValue(expr, engine()->evaluate(expr)); // Clear any exceptions occurred during locals evaluation. engine()->clearExceptions(); QByteArray reply; QDataStream rs(&reply, QIODevice::WriteOnly); - rs << QByteArray("RESULT") << id << expr << val; + rs << QByteArray("RESULT") << id << data; sendMessage(reply); state = oldState; @@ -265,18 +334,25 @@ void JSDebuggerAgent::stopped() } backtrace.append(qMakePair(functionName, qMakePair( QUrl(info.fileName()).toLocalFile(), info.lineNumber() ) ) ); } - QList<QPair<QString, QVariant> > watches; + QList<JSAgentWatchData> watches; foreach (const QString &expr, watchExpressions) { - watches << qMakePair(expr, engine()->evaluate(expr).toVariant()); + watches << JSAgentWatchData::fromScriptValue(expr, engine()->evaluate(expr)); + } + + QList<JSAgentWatchData> locals; + QScriptValue activationObject = engine()->currentContext()->activationObject(); + QScriptValueIterator it(activationObject); + while (it.hasNext()) { + it.next(); + locals << JSAgentWatchData::fromScriptValue(it.name(), it.value()); } // Clear any exceptions occurred during locals evaluation. engine()->clearExceptions(); - QByteArray reply; QDataStream rs(&reply, QIODevice::WriteOnly); - rs << QByteArray("STOPPED") << backtrace << watches << engine()->currentContext()->activationObject().toVariant(); + rs << QByteArray("STOPPED") << backtrace << watches << locals; sendMessage(reply); loop.exec(QEventLoop::ExcludeUserInputEvents); -- GitLab