Commit bfa609e3 authored by Olivier Goffart's avatar Olivier Goffart
Browse files

QML JS Debugger: Better way to stream watch data

QVariant does not work as they can contain custom type that corrypt the stream
parent 0340d6ad
......@@ -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;
......
......@@ -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;
......
......@@ -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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment