Commit 9720c0d7 authored by Aurindam Jana's avatar Aurindam Jana

QmlDebugging: Show Object Tree in Console

Show QML/Javascript objects as a tree when
evaluated in the console.

Change-Id: I42901bf9bda3f18fb9fb1ca309a8370ccbe37c0a
Reviewed-by: default avatarKai Koehne <kai.koehne@nokia.com>
parent 4667371b
...@@ -804,11 +804,11 @@ void QmlEngine::onDebugQueryStateChanged( ...@@ -804,11 +804,11 @@ void QmlEngine::onDebugQueryStateChanged(
QmlJsDebugClient::QDeclarativeDebugExpressionQuery *query = QmlJsDebugClient::QDeclarativeDebugExpressionQuery *query =
qobject_cast<QmlJsDebugClient::QDeclarativeDebugExpressionQuery *>( qobject_cast<QmlJsDebugClient::QDeclarativeDebugExpressionQuery *>(
sender()); sender());
if (query && state != QmlJsDebugClient::QDeclarativeDebugQuery::Error) if (query && state != QmlJsDebugClient::QDeclarativeDebugQuery::Error) {
qtMessageLogHandler()-> QtMessageLogItem *item = constructLogItemTree(query->result());
appendItem(new QtMessageLogItem(QtMessageLogHandler::UndefinedType, if (item)
query->result().toString())); qtMessageLogHandler()->appendItem(item);
else } else
qtMessageLogHandler()-> qtMessageLogHandler()->
appendItem(new QtMessageLogItem(QtMessageLogHandler::ErrorType, appendItem(new QtMessageLogItem(QtMessageLogHandler::ErrorType,
_("Error evaluating expression."))); _("Error evaluating expression.")));
...@@ -1050,6 +1050,47 @@ bool QmlEngine::canEvaluateScript(const QString &script) ...@@ -1050,6 +1050,47 @@ bool QmlEngine::canEvaluateScript(const QString &script)
return d->m_interpreter.canEvaluate(); return d->m_interpreter.canEvaluate();
} }
QtMessageLogItem *QmlEngine::constructLogItemTree(
const QVariant &result, const QString &key)
{
if (!result.isValid())
return 0;
QtMessageLogItem *item = new QtMessageLogItem();
if (result.type() == QVariant::Map) {
if (key.isEmpty())
item->setText(_("Object"));
else
item->setText(QString(_("%1: Object")).arg(key));
QMapIterator<QString, QVariant> i(result.toMap());
while (i.hasNext()) {
i.next();
QtMessageLogItem *child = constructLogItemTree(i.value(), i.key());
if (child)
item->insertChild(item->childCount(), child);
}
} else if (result.type() == QVariant::List) {
if (key.isEmpty())
item->setText(_("List"));
else
item->setText(QString(_("[%1] : List")).arg(key));
QVariantList resultList = result.toList();
for (int i = 0; i < resultList.count(); i++) {
QtMessageLogItem *child = constructLogItemTree(resultList.at(i),
QString::number(i));
if (child)
item->insertChild(item->childCount(), child);
}
} else if (result.canConvert(QVariant::String)) {
item->setText(result.toString());
} else {
item->setText(_("Unknown Value"));
}
return item;
}
QmlAdapter *QmlEngine::adapter() const QmlAdapter *QmlEngine::adapter() const
{ {
return &d->m_adapter; return &d->m_adapter;
......
...@@ -53,6 +53,7 @@ class QmlAdapter; ...@@ -53,6 +53,7 @@ class QmlAdapter;
namespace Internal { namespace Internal {
class QtMessageLogItem;
class QmlEnginePrivate; class QmlEnginePrivate;
class QmlEngine : public DebuggerEngine class QmlEngine : public DebuggerEngine
...@@ -182,6 +183,8 @@ private: ...@@ -182,6 +183,8 @@ private:
void updateEditor(Core::IEditor *editor, const QTextDocument *document); void updateEditor(Core::IEditor *editor, const QTextDocument *document);
bool canEvaluateScript(const QString &script); bool canEvaluateScript(const QString &script);
QtMessageLogItem *constructLogItemTree(const QVariant &result,
const QString &key = QString());
private: private:
friend class QmlCppEngine; friend class QmlCppEngine;
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include "breakhandler.h" #include "breakhandler.h"
#include "qmlengine.h" #include "qmlengine.h"
#include "stackhandler.h" #include "stackhandler.h"
#include "qtmessageloghandler.h"
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
...@@ -68,9 +69,11 @@ namespace Internal { ...@@ -68,9 +69,11 @@ namespace Internal {
typedef QPair<QByteArray, QByteArray> WatchDataPair; typedef QPair<QByteArray, QByteArray> WatchDataPair;
struct QmlV8ObjectData { struct QmlV8ObjectData {
int handle;
QByteArray name;
QByteArray type; QByteArray type;
QVariant value; QVariant value;
QVariant properties; QVariantList properties;
}; };
class QmlV8DebuggerClientPrivate class QmlV8DebuggerClientPrivate
...@@ -117,7 +120,7 @@ public: ...@@ -117,7 +120,7 @@ public:
//void profile(ProfileCommand command); //NOT SUPPORTED //void profile(ProfileCommand command); //NOT SUPPORTED
void gc(); void gc();
QmlV8ObjectData extractData(const QVariant &data); QmlV8ObjectData extractData(const QVariant &data, const QVariant &refsVal);
void clearCache(); void clearCache();
void logSendMessage(const QString &msg) const; void logSendMessage(const QString &msg) const;
...@@ -126,10 +129,12 @@ public: ...@@ -126,10 +129,12 @@ public:
//TODO:: remove this method //TODO:: remove this method
void reformatRequest(QByteArray &request); void reformatRequest(QByteArray &request);
QtMessageLogItem *constructLogItemTree(const QmlV8ObjectData &objectData,
const QVariant &refsVal);
private: private:
QByteArray packMessage(const QByteArray &type, const QByteArray &message = QByteArray()); QByteArray packMessage(const QByteArray &type, const QByteArray &message = QByteArray());
QScriptValue initObject(); QScriptValue initObject();
QVariant valueFromRef(int handle, const QVariant &refsVal, bool *success);
public: public:
QmlV8DebuggerClient *q; QmlV8DebuggerClient *q;
...@@ -149,7 +154,6 @@ public: ...@@ -149,7 +154,6 @@ public:
//Cache //Cache
QStringList watchedExpressions; QStringList watchedExpressions;
QVariant refsVal;
QList<int> currentFrameScopes; QList<int> currentFrameScopes;
//TODO: remove this flag //TODO: remove this flag
...@@ -779,7 +783,8 @@ void QmlV8DebuggerClientPrivate::gc() ...@@ -779,7 +783,8 @@ void QmlV8DebuggerClientPrivate::gc()
q->sendMessage(packMessage(V8REQUEST, jsonMessage.toString().toUtf8())); q->sendMessage(packMessage(V8REQUEST, jsonMessage.toString().toUtf8()));
} }
QmlV8ObjectData QmlV8DebuggerClientPrivate::extractData(const QVariant &data) QmlV8ObjectData QmlV8DebuggerClientPrivate::extractData(const QVariant &data,
const QVariant &refsVal)
{ {
// { "handle" : <handle>, // { "handle" : <handle>,
// "type" : <"undefined", "null", "boolean", "number", "string", "object", "function" or "frame"> // "type" : <"undefined", "null", "boolean", "number", "string", "object", "function" or "frame">
...@@ -834,41 +839,56 @@ QmlV8ObjectData QmlV8DebuggerClientPrivate::extractData(const QVariant &data) ...@@ -834,41 +839,56 @@ QmlV8ObjectData QmlV8DebuggerClientPrivate::extractData(const QVariant &data)
QmlV8ObjectData objectData; QmlV8ObjectData objectData;
const QVariantMap dataMap = data.toMap(); const QVariantMap dataMap = data.toMap();
QString type = dataMap.value(_(TYPE)).toString();
if (type == _("undefined")) { objectData.name = dataMap.value(_(NAME)).toByteArray();
objectData.type = QByteArray("undefined");
objectData.value = QVariant(_("undefined")); if (dataMap.contains(_(REF))) {
objectData.handle = dataMap.value(_(REF)).toInt();
bool success;
QVariant dataFromRef = valueFromRef(objectData.handle, refsVal, &success);
if (success) {
QmlV8ObjectData data = extractData(dataFromRef, refsVal);
objectData.type = data.type;
objectData.value = data.value;
objectData.properties = data.properties;
}
} else {
QString type = dataMap.value(_(TYPE)).toString();
if (type == _("undefined")) {
objectData.type = QByteArray("undefined");
objectData.value = QVariant(_("undefined"));
} else if (type == _("null")) { } else if (type == _("null")) {
objectData.type = QByteArray("null"); objectData.type = QByteArray("null");
objectData.value= QVariant(_("null")); objectData.value= QVariant(_("null"));
} else if (type == _("boolean")) { } else if (type == _("boolean")) {
objectData.type = QByteArray("boolean"); objectData.type = QByteArray("boolean");
objectData.value = dataMap.value(_(VALUE)); objectData.value = dataMap.value(_(VALUE));
} else if (type == _("number")) { } else if (type == _("number")) {
objectData.type = QByteArray("number"); objectData.type = QByteArray("number");
objectData.value = dataMap.value(_(VALUE)); objectData.value = dataMap.value(_(VALUE));
} else if (type == _("string")) { } else if (type == _("string")) {
objectData.type = QByteArray("string"); objectData.type = QByteArray("string");
objectData.value = dataMap.value(_(VALUE)); objectData.value = dataMap.value(_(VALUE));
} else if (type == _("object")) { } else if (type == _("object")) {
objectData.type = QByteArray("object"); objectData.type = QByteArray("object");
objectData.value = dataMap.value(_("className")); objectData.value = dataMap.value(_("className"));
objectData.properties = dataMap.value(_("properties")); objectData.properties = dataMap.value(_("properties")).toList();
} else if (type == _("function")) { } else if (type == _("function")) {
objectData.type = QByteArray("function"); objectData.type = QByteArray("function");
objectData.value = dataMap.value(_(NAME)); objectData.value = dataMap.value(_(NAME));
objectData.properties = dataMap.value(_("properties")); objectData.properties = dataMap.value(_("properties")).toList();
} else if (type == _("script")) { } else if (type == _("script")) {
objectData.type = QByteArray("script"); objectData.type = QByteArray("script");
objectData.value = dataMap.value(_(NAME)); objectData.value = dataMap.value(_(NAME));
}
} }
return objectData; return objectData;
...@@ -877,7 +897,6 @@ QmlV8ObjectData QmlV8DebuggerClientPrivate::extractData(const QVariant &data) ...@@ -877,7 +897,6 @@ QmlV8ObjectData QmlV8DebuggerClientPrivate::extractData(const QVariant &data)
void QmlV8DebuggerClientPrivate::clearCache() void QmlV8DebuggerClientPrivate::clearCache()
{ {
watchedExpressions.clear(); watchedExpressions.clear();
refsVal.clear();
currentFrameScopes.clear(); currentFrameScopes.clear();
} }
...@@ -903,6 +922,24 @@ QScriptValue QmlV8DebuggerClientPrivate::initObject() ...@@ -903,6 +922,24 @@ QScriptValue QmlV8DebuggerClientPrivate::initObject()
return jsonVal; return jsonVal;
} }
QVariant QmlV8DebuggerClientPrivate::valueFromRef(int handle,
const QVariant &refsVal,
bool *success)
{
*success = false;
QVariant variant;
const QVariantList refs = refsVal.toList();
foreach (const QVariant &ref, refs) {
const QVariantMap refData = ref.toMap();
if (refData.value(_(HANDLE)).toInt() == handle) {
variant = refData;
*success = true;
break;
}
}
return variant;
}
void QmlV8DebuggerClientPrivate::logSendMessage(const QString &msg) const void QmlV8DebuggerClientPrivate::logSendMessage(const QString &msg) const
{ {
if (engine) if (engine)
...@@ -956,6 +993,33 @@ void QmlV8DebuggerClientPrivate::reformatRequest(QByteArray &request) ...@@ -956,6 +993,33 @@ void QmlV8DebuggerClientPrivate::reformatRequest(QByteArray &request)
} }
} }
QtMessageLogItem *QmlV8DebuggerClientPrivate::constructLogItemTree(
const QmlV8ObjectData &objectData,
const QVariant &refsVal)
{
if (!objectData.value.isValid())
return 0;
QString text;
if (objectData.name.isEmpty())
text = objectData.value.toString();
else
text = QString(_("%1: %2")).arg(QString::fromAscii(objectData.name))
.arg(objectData.value.toString());
QtMessageLogItem *item = new QtMessageLogItem(
QtMessageLogHandler::UndefinedType, text);
foreach (const QVariant &property, objectData.properties) {
QtMessageLogItem *child = constructLogItemTree(
extractData(property, refsVal), refsVal);
if (child)
item->insertChild(item->childCount(), child);
}
return item;
}
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
// //
// QmlV8DebuggerClient // QmlV8DebuggerClient
...@@ -1525,7 +1589,6 @@ void QmlV8DebuggerClient::messageReceived(const QByteArray &data) ...@@ -1525,7 +1589,6 @@ void QmlV8DebuggerClient::messageReceived(const QByteArray &data)
} }
} }
void QmlV8DebuggerClient::updateStack(const QVariant &bodyVal, const QVariant &refsVal) void QmlV8DebuggerClient::updateStack(const QVariant &bodyVal, const QVariant &refsVal)
{ {
// { "seq" : <number>, // { "seq" : <number>,
...@@ -1600,27 +1663,18 @@ StackFrame QmlV8DebuggerClient::insertStackFrame(const QVariant &bodyVal, const ...@@ -1600,27 +1663,18 @@ StackFrame QmlV8DebuggerClient::insertStackFrame(const QVariant &bodyVal, const
StackFrame stackFrame; StackFrame stackFrame;
stackFrame.level = body.value(_("index")).toInt(); stackFrame.level = body.value(_("index")).toInt();
QVariantMap func = body.value(_("func")).toMap(); QmlV8ObjectData objectData = d->extractData(body.value(_("func")), refsVal);
if (func.contains(_(REF))) { QString functionName = objectData.value.toString();
func = valueFromRef(func.value(_(REF)).toInt(), refsVal).toMap();
}
QString functionName = d->extractData(QVariant(func)).value.toString();
if (functionName.isEmpty()) if (functionName.isEmpty())
functionName = tr("anonymous function"); functionName = tr("anonymous function");
stackFrame.function = functionName; stackFrame.function = functionName;
QVariantMap file = body.value(_("script")).toMap(); objectData = d->extractData(body.value(_("script")), refsVal);
if (file.contains(_(REF))) { stackFrame.file = d->engine->toFileInProject(objectData.value.toString());
file = valueFromRef(file.value(_(REF)).toInt(), refsVal).toMap();
}
stackFrame.file = d->engine->toFileInProject(d->extractData(QVariant(file)).value.toString());
stackFrame.usable = QFileInfo(stackFrame.file).isReadable(); stackFrame.usable = QFileInfo(stackFrame.file).isReadable();
QVariantMap receiver = body.value(_("receiver")).toMap(); objectData = d->extractData(body.value(_("receiver")), refsVal);
if (receiver.contains(_(REF))) { stackFrame.to = objectData.value.toString();
receiver = valueFromRef(receiver.value(_(REF)).toInt(), refsVal).toMap();
}
stackFrame.to = d->extractData(QVariant(receiver)).value.toString();
stackFrame.line = body.value(_("line")).toInt() + 1; stackFrame.line = body.value(_("line")).toInt() + 1;
...@@ -1665,22 +1719,18 @@ void QmlV8DebuggerClient::setCurrentFrameDetails(const QVariant &bodyVal, const ...@@ -1665,22 +1719,18 @@ void QmlV8DebuggerClient::setCurrentFrameDetails(const QVariant &bodyVal, const
int frameIndex = currentFrame.value(QLatin1String("index")).toInt(); int frameIndex = currentFrame.value(QLatin1String("index")).toInt();
d->clearCache(); d->clearCache();
d->refsVal = refsVal;
//Set "this" variable //Set "this" variable
{ {
WatchData data; WatchData data;
data.exp = QByteArray("this"); data.exp = QByteArray("this");
data.name = QLatin1String(data.exp); data.name = QLatin1String(data.exp);
data.iname = QByteArray("local.") + data.exp; data.iname = QByteArray("local.") + data.exp;
QVariantMap receiver = currentFrame.value(_("receiver")).toMap(); QmlV8ObjectData objectData = d->extractData(
if (receiver.contains(_(REF))) { currentFrame.value(_("receiver")), refsVal);
receiver = valueFromRef(receiver.value(_(REF)).toInt(), refsVal).toMap(); data.id = objectData.handle;
} data.type = objectData.type;
data.id = receiver.value(_("handle")).toInt(); data.value = objectData.value.toString();
QmlV8ObjectData receiverData = d->extractData(QVariant(receiver)); data.setHasChildren(objectData.properties.count());
data.type = receiverData.type;
data.value = receiverData.value.toString();
data.setHasChildren(receiverData.properties.toList().count());
d->engine->watchHandler()->beginCycle(); d->engine->watchHandler()->beginCycle();
d->engine->watchHandler()->insertData(data); d->engine->watchHandler()->insertData(data);
d->engine->watchHandler()->endCycle(); d->engine->watchHandler()->endCycle();
...@@ -1730,20 +1780,14 @@ void QmlV8DebuggerClient::updateScope(const QVariant &bodyVal, const QVariant &r ...@@ -1730,20 +1780,14 @@ void QmlV8DebuggerClient::updateScope(const QVariant &bodyVal, const QVariant &r
if (bodyMap.value(_("frameIndex")).toInt() != stackHandler->currentIndex()) if (bodyMap.value(_("frameIndex")).toInt() != stackHandler->currentIndex())
return; return;
QVariantMap object = bodyMap.value(_("object")).toMap(); QmlV8ObjectData objectData = d->extractData(bodyMap.value(_("object")), refsVal);
if (object.contains(_(REF))) {
object = valueFromRef(object.value(_(REF)).toInt(),
refsVal).toMap();
}
const QVariantList properties = object.value(_("properties")).toList();
QList<int> handlesToLookup; QList<int> handlesToLookup;
QList<WatchData> locals; QList<WatchData> locals;
foreach (const QVariant &property, properties) { foreach (const QVariant &property, objectData.properties) {
QVariantMap localData = property.toMap(); QmlV8ObjectData localData = d->extractData(property, refsVal);
WatchData data; WatchData data;
data.exp = localData.value(_(NAME)).toByteArray(); data.exp = localData.name;
//Check for v8 specific local data //Check for v8 specific local data
if (data.exp.startsWith('.') || data.exp.isEmpty()) if (data.exp.startsWith('.') || data.exp.isEmpty())
continue; continue;
...@@ -1751,22 +1795,16 @@ void QmlV8DebuggerClient::updateScope(const QVariant &bodyVal, const QVariant &r ...@@ -1751,22 +1795,16 @@ void QmlV8DebuggerClient::updateScope(const QVariant &bodyVal, const QVariant &r
data.name = QLatin1String(data.exp); data.name = QLatin1String(data.exp);
data.iname = QByteArray("local.") + data.exp; data.iname = QByteArray("local.") + data.exp;
int handle = localData.value(_(REF)).toInt(); int handle = localData.handle;
localData = valueFromRef(handle, d->refsVal).toMap(); if (localData.value.isValid()) {
if (localData.isEmpty()) { data.id = handle;
data.type = localData.type;
data.value = localData.value.toString();
data.setHasChildren(localData.properties.count());
locals << data;
} else {
handlesToLookup << handle; handlesToLookup << handle;
d->localsAndWatchers.insert(handle, data.exp); d->localsAndWatchers.insert(handle, data.exp);
} else {
data.id = localData.value(_(HANDLE)).toInt();
QmlV8ObjectData objectData = d->extractData(QVariant(localData));
data.type = objectData.type;
data.value = objectData.value.toString();
data.setHasChildren(objectData.properties.toList().count());
locals << data;
} }
} }
...@@ -1797,13 +1835,7 @@ void QmlV8DebuggerClient::updateEvaluationResult(int sequence, bool success, con ...@@ -1797,13 +1835,7 @@ void QmlV8DebuggerClient::updateEvaluationResult(int sequence, bool success, con
d->scope(index); d->scope(index);
} else { } else {
QVariantMap bodyMap = bodyVal.toMap(); QmlV8ObjectData body = d->extractData(bodyVal, refsVal);
if (bodyMap.contains(_(REF))) {
bodyMap = valueFromRef(bodyMap.value(_(REF)).toInt(),
refsVal).toMap();
}
QmlV8ObjectData body = d->extractData(QVariant(bodyMap));
QString exp = d->evaluatingExpression.take(sequence); QString exp = d->evaluatingExpression.take(sequence);
if (d->watchedExpressions.contains(exp)) { if (d->watchedExpressions.contains(exp)) {
QByteArray iname = d->engine->watchHandler()->watcherName(exp.toLatin1()); QByteArray iname = d->engine->watchHandler()->watcherName(exp.toLatin1());
...@@ -1812,11 +1844,11 @@ void QmlV8DebuggerClient::updateEvaluationResult(int sequence, bool success, con ...@@ -1812,11 +1844,11 @@ void QmlV8DebuggerClient::updateEvaluationResult(int sequence, bool success, con
data.exp = exp.toLatin1(); data.exp = exp.toLatin1();
data.name = exp; data.name = exp;
data.iname = iname; data.iname = iname;
data.id = bodyMap.value(_(HANDLE)).toInt(); data.id = body.handle;
if (success) { if (success) {
data.type = body.type; data.type = body.type;
data.value = body.value.toString(); data.value = body.value.toString();
data.hasChildren = body.properties.toList().count(); data.hasChildren = body.properties.count();
} else { } else {
//Do not set type since it is unknown //Do not set type since it is unknown
data.setError(body.value.toString()); data.setError(body.value.toString());
...@@ -1828,7 +1860,9 @@ void QmlV8DebuggerClient::updateEvaluationResult(int sequence, bool success, con ...@@ -1828,7 +1860,9 @@ void QmlV8DebuggerClient::updateEvaluationResult(int sequence, bool success, con
d->engine->watchHandler()->endCycle(); d->engine->watchHandler()->endCycle();
} else { } else {
d->engine->showMessage(body.value.toString(), QtMessageLogOutput); QtMessageLogItem *item = d->constructLogItemTree(body, refsVal);
if (item)
d->engine->qtMessageLogHandler()->appendItem(item);
//Update the locals //Update the locals
foreach (int index, d->currentFrameScopes) foreach (int index, d->currentFrameScopes)
d->scope(index); d->scope(index);
...@@ -1886,20 +1920,6 @@ void QmlV8DebuggerClient::updateBreakpoints(const QVariant &bodyVal) ...@@ -1886,20 +1920,6 @@ void QmlV8DebuggerClient::updateBreakpoints(const QVariant &bodyVal)
} }
} }
QVariant QmlV8DebuggerClient::valueFromRef(int handle, const QVariant &refsVal)
{
QVariant variant;