1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativeenginedebugservice_p.h"
44 #include "qdeclarativedebugstatesdelegate_p.h"
45 #include <private/qdeclarativeboundsignal_p.h>
46 #include <qdeclarativeengine.h>
47 #include <private/qdeclarativemetatype_p.h>
48 #include <qdeclarativeproperty.h>
49 #include <private/qdeclarativeproperty_p.h>
50 #include <private/qdeclarativebinding_p.h>
51 #include <private/qdeclarativecontext_p.h>
52 #include <private/qdeclarativewatcher_p.h>
53 #include <private/qdeclarativevaluetype_p.h>
54 #include <private/qdeclarativevmemetaobject_p.h>
55 #include <private/qdeclarativeexpression_p.h>
57 #include <QtCore/qdebug.h>
58 #include <QtCore/qmetaobject.h>
62 Q_GLOBAL_STATIC(QDeclarativeEngineDebugService, qmlEngineDebugService);
64 QDeclarativeEngineDebugService *QDeclarativeEngineDebugService::instance()
66 return qmlEngineDebugService();
69 QDeclarativeEngineDebugService::QDeclarativeEngineDebugService(QObject *parent)
70 : QDeclarativeDebugService(QLatin1String("QDeclarativeEngine"), 1, parent),
71 m_watch(new QDeclarativeWatcher(this)),
74 QObject::connect(m_watch, SIGNAL(propertyChanged(int,int,QMetaProperty,QVariant)),
75 this, SLOT(propertyChanged(int,int,QMetaProperty,QVariant)));
80 QDeclarativeEngineDebugService::~QDeclarativeEngineDebugService()
82 delete m_statesDelegate;
85 QDataStream &operator<<(QDataStream &ds,
86 const QDeclarativeEngineDebugService::QDeclarativeObjectData &data)
88 ds << data.url << data.lineNumber << data.columnNumber << data.idString
89 << data.objectName << data.objectType << data.objectId << data.contextId;
93 QDataStream &operator>>(QDataStream &ds,
94 QDeclarativeEngineDebugService::QDeclarativeObjectData &data)
96 ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString
97 >> data.objectName >> data.objectType >> data.objectId >> data.contextId;
101 QDataStream &operator<<(QDataStream &ds,
102 const QDeclarativeEngineDebugService::QDeclarativeObjectProperty &data)
104 ds << (int)data.type << data.name << data.value << data.valueTypeName
105 << data.binding << data.hasNotifySignal;
109 QDataStream &operator>>(QDataStream &ds,
110 QDeclarativeEngineDebugService::QDeclarativeObjectProperty &data)
113 ds >> type >> data.name >> data.value >> data.valueTypeName
114 >> data.binding >> data.hasNotifySignal;
115 data.type = (QDeclarativeEngineDebugService::QDeclarativeObjectProperty::Type)type;
119 static inline bool isSignalPropertyName(const QString &signalName)
121 // see QmlCompiler::isSignalPropertyName
122 return signalName.length() >= 3 && signalName.startsWith(QLatin1String("on")) &&
123 signalName.at(2).isLetter() && signalName.at(2).isUpper();
126 static bool hasValidSignal(QObject *object, const QString &propertyName)
128 if (!isSignalPropertyName(propertyName))
131 QString signalName = propertyName.mid(2);
132 signalName[0] = signalName.at(0).toLower();
134 int sigIdx = QDeclarativePropertyPrivate::findSignalByName(object->metaObject(), signalName.toLatin1()).methodIndex();
142 QDeclarativeEngineDebugService::QDeclarativeObjectProperty
143 QDeclarativeEngineDebugService::propertyData(QObject *obj, int propIdx)
145 QDeclarativeObjectProperty rv;
147 QMetaProperty prop = obj->metaObject()->property(propIdx);
149 rv.type = QDeclarativeObjectProperty::Unknown;
150 rv.valueTypeName = QString::fromUtf8(prop.typeName());
151 rv.name = QString::fromUtf8(prop.name());
152 rv.hasNotifySignal = prop.hasNotifySignal();
153 QDeclarativeAbstractBinding *binding =
154 QDeclarativePropertyPrivate::binding(QDeclarativeProperty(obj, rv.name));
156 rv.binding = binding->expression();
158 if (QDeclarativeValueTypeFactory::isValueType(prop.userType())) {
159 rv.type = QDeclarativeObjectProperty::Basic;
160 } else if (QDeclarativeMetaType::isQObject(prop.userType())) {
161 rv.type = QDeclarativeObjectProperty::Object;
162 } else if (QDeclarativeMetaType::isList(prop.userType())) {
163 rv.type = QDeclarativeObjectProperty::List;
167 if (rv.type != QDeclarativeObjectProperty::Unknown && prop.userType() != 0) {
168 value = prop.read(obj);
170 rv.value = valueContents(value);
175 QVariant QDeclarativeEngineDebugService::valueContents(const QVariant &value) const
177 int userType = value.userType();
179 if (value.type() == QVariant::List) {
180 QVariantList contents;
181 QVariantList list = value.toList();
182 int count = list.size();
183 for (int i = 0; i < count; i++)
184 contents << valueContents(list.at(i));
188 if (QDeclarativeValueTypeFactory::isValueType(userType))
191 if (QDeclarativeMetaType::isQObject(userType)) {
192 QObject *o = QDeclarativeMetaType::toQObject(value);
194 QString name = o->objectName();
196 name = QLatin1String("<unnamed object>");
201 return QLatin1String("<unknown value>");
204 void QDeclarativeEngineDebugService::buildObjectDump(QDataStream &message,
205 QObject *object, bool recur, bool dumpProperties)
207 message << objectData(object);
209 QObjectList children = object->children();
211 int childrenCount = children.count();
212 for (int ii = 0; ii < children.count(); ++ii) {
213 if (qobject_cast<QDeclarativeContext*>(children[ii]) || QDeclarativeBoundSignal::cast(children[ii]))
217 message << childrenCount << recur;
219 QList<QDeclarativeObjectProperty> fakeProperties;
221 for (int ii = 0; ii < children.count(); ++ii) {
222 QObject *child = children.at(ii);
223 if (qobject_cast<QDeclarativeContext*>(child))
225 QDeclarativeBoundSignal *signal = QDeclarativeBoundSignal::cast(child);
229 QDeclarativeObjectProperty prop;
230 prop.type = QDeclarativeObjectProperty::SignalProperty;
231 prop.hasNotifySignal = false;
232 QDeclarativeExpression *expr = signal->expression();
234 prop.value = expr->expression();
235 QObject *scope = expr->scopeObject();
237 QString sig = QLatin1String(scope->metaObject()->method(signal->index()).signature());
238 int lparen = sig.indexOf(QLatin1Char('('));
240 QString methodName = sig.mid(0, lparen);
241 prop.name = QLatin1String("on") + methodName[0].toUpper()
246 fakeProperties << prop;
249 buildObjectDump(message, child, recur, dumpProperties);
251 message << objectData(child);
255 if (!dumpProperties) {
260 QList<int> propertyIndexes;
261 for (int ii = 0; ii < object->metaObject()->propertyCount(); ++ii) {
262 if (object->metaObject()->property(ii).isScriptable())
263 propertyIndexes << ii;
266 message << propertyIndexes.size() + fakeProperties.count();
268 for (int ii = 0; ii < propertyIndexes.size(); ++ii)
269 message << propertyData(object, propertyIndexes.at(ii));
271 for (int ii = 0; ii < fakeProperties.count(); ++ii)
272 message << fakeProperties[ii];
275 void QDeclarativeEngineDebugService::prepareDeferredObjects(QObject *obj)
277 qmlExecuteDeferred(obj);
279 QObjectList children = obj->children();
280 for (int ii = 0; ii < children.count(); ++ii) {
281 QObject *child = children.at(ii);
282 prepareDeferredObjects(child);
287 void QDeclarativeEngineDebugService::buildObjectList(QDataStream &message, QDeclarativeContext *ctxt)
289 QDeclarativeContextData *p = QDeclarativeContextData::get(ctxt);
291 QString ctxtName = ctxt->objectName();
292 int ctxtId = QDeclarativeDebugService::idForObject(ctxt);
294 message << ctxtName << ctxtId;
298 QDeclarativeContextData *child = p->childContexts;
301 child = child->nextChild;
306 child = p->childContexts;
308 buildObjectList(message, child->asQDeclarativeContext());
309 child = child->nextChild;
312 // Clean deleted objects
313 QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(ctxt);
314 for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) {
315 if (!ctxtPriv->instances.at(ii)) {
316 ctxtPriv->instances.removeAt(ii);
321 message << ctxtPriv->instances.count();
322 for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) {
323 message << objectData(ctxtPriv->instances.at(ii));
327 void QDeclarativeEngineDebugService::buildStatesList(QDeclarativeContext *ctxt, bool cleanList)
329 if (m_statesDelegate)
330 m_statesDelegate->buildStatesList(ctxt, cleanList);
333 QDeclarativeEngineDebugService::QDeclarativeObjectData
334 QDeclarativeEngineDebugService::objectData(QObject *object)
336 QDeclarativeData *ddata = QDeclarativeData::get(object);
337 QDeclarativeObjectData rv;
338 if (ddata && ddata->outerContext) {
339 rv.url = ddata->outerContext->url;
340 rv.lineNumber = ddata->lineNumber;
341 rv.columnNumber = ddata->columnNumber;
344 rv.columnNumber = -1;
347 QDeclarativeContext *context = qmlContext(object);
349 QDeclarativeContextData *cdata = QDeclarativeContextData::get(context);
351 rv.idString = cdata->findObjectId(object);
354 rv.objectName = object->objectName();
355 rv.objectId = QDeclarativeDebugService::idForObject(object);
356 rv.contextId = QDeclarativeDebugService::idForObject(qmlContext(object));
358 QDeclarativeType *type = QDeclarativeMetaType::qmlType(object->metaObject());
360 QString typeName = type->qmlTypeName();
361 int lastSlash = typeName.lastIndexOf(QLatin1Char('/'));
362 rv.objectType = lastSlash < 0 ? typeName : typeName.mid(lastSlash+1);
364 rv.objectType = QString::fromUtf8(object->metaObject()->className());
365 int marker = rv.objectType.indexOf(QLatin1String("_QMLTYPE_"));
367 rv.objectType = rv.objectType.left(marker);
373 void QDeclarativeEngineDebugService::messageReceived(const QByteArray &message)
375 QMetaObject::invokeMethod(this, "processMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message));
378 void QDeclarativeEngineDebugService::processMessage(const QByteArray &message)
380 QDataStream ds(message);
385 if (type == "LIST_ENGINES") {
390 QDataStream rs(&reply, QIODevice::WriteOnly);
391 rs << QByteArray("LIST_ENGINES_R");
392 rs << queryId << m_engines.count();
394 for (int ii = 0; ii < m_engines.count(); ++ii) {
395 QDeclarativeEngine *engine = m_engines.at(ii);
397 QString engineName = engine->objectName();
398 int engineId = QDeclarativeDebugService::idForObject(engine);
400 rs << engineName << engineId;
404 } else if (type == "LIST_OBJECTS") {
407 ds >> queryId >> engineId;
409 QDeclarativeEngine *engine =
410 qobject_cast<QDeclarativeEngine *>(QDeclarativeDebugService::objectForId(engineId));
413 QDataStream rs(&reply, QIODevice::WriteOnly);
414 rs << QByteArray("LIST_OBJECTS_R") << queryId;
417 buildObjectList(rs, engine->rootContext());
418 buildStatesList(engine->rootContext(), true);
422 } else if (type == "FETCH_OBJECT") {
426 bool dumpProperties = true;
428 ds >> queryId >> objectId >> recurse >> dumpProperties;
430 QObject *object = QDeclarativeDebugService::objectForId(objectId);
433 QDataStream rs(&reply, QIODevice::WriteOnly);
434 rs << QByteArray("FETCH_OBJECT_R") << queryId;
438 prepareDeferredObjects(object);
439 buildObjectDump(rs, object, recurse, dumpProperties);
443 } else if (type == "WATCH_OBJECT") {
447 ds >> queryId >> objectId;
448 bool ok = m_watch->addWatch(queryId, objectId);
451 QDataStream rs(&reply, QIODevice::WriteOnly);
452 rs << QByteArray("WATCH_OBJECT_R") << queryId << ok;
455 } else if (type == "WATCH_PROPERTY") {
460 ds >> queryId >> objectId >> property;
461 bool ok = m_watch->addWatch(queryId, objectId, property);
464 QDataStream rs(&reply, QIODevice::WriteOnly);
465 rs << QByteArray("WATCH_PROPERTY_R") << queryId << ok;
468 } else if (type == "WATCH_EXPR_OBJECT") {
473 ds >> queryId >> debugId >> expr;
474 bool ok = m_watch->addWatch(queryId, debugId, expr);
477 QDataStream rs(&reply, QIODevice::WriteOnly);
478 rs << QByteArray("WATCH_EXPR_OBJECT_R") << queryId << ok;
480 } else if (type == "NO_WATCH") {
484 m_watch->removeWatch(queryId);
485 } else if (type == "EVAL_EXPRESSION") {
490 ds >> queryId >> objectId >> expr;
492 QObject *object = QDeclarativeDebugService::objectForId(objectId);
493 QDeclarativeContext *context = qmlContext(object);
495 if (object && context) {
496 QDeclarativeExpression exprObj(context, object, expr);
497 bool undefined = false;
498 QVariant value = exprObj.evaluate(&undefined);
500 result = QLatin1String("<undefined>");
502 result = valueContents(value);
504 result = QLatin1String("<unknown context>");
508 QDataStream rs(&reply, QIODevice::WriteOnly);
509 rs << QByteArray("EVAL_EXPRESSION_R") << queryId << result;
512 } else if (type == "SET_BINDING") {
514 QString propertyName;
519 ds >> objectId >> propertyName >> expr >> isLiteralValue;
520 if (!ds.atEnd()) { // backward compatibility from 2.1, 2.2
521 ds >> filename >> line;
523 setBinding(objectId, propertyName, expr, isLiteralValue, filename, line);
524 } else if (type == "RESET_BINDING") {
526 QString propertyName;
527 ds >> objectId >> propertyName;
528 resetBinding(objectId, propertyName);
529 } else if (type == "SET_METHOD_BODY") {
533 ds >> objectId >> methodName >> methodBody;
534 setMethodBody(objectId, methodName, methodBody);
538 void QDeclarativeEngineDebugService::setBinding(int objectId,
539 const QString &propertyName,
540 const QVariant &expression,
546 QObject *object = objectForId(objectId);
547 QDeclarativeContext *context = qmlContext(object);
549 if (object && context) {
550 QDeclarativeProperty property(object, propertyName, context);
551 if (property.isValid()) {
553 bool inBaseState = true;
554 if (m_statesDelegate) {
555 m_statesDelegate->updateBinding(context, property, expression, isLiteralValue,
556 filename, line, column, &inBaseState);
560 if (isLiteralValue) {
561 property.write(expression);
562 } else if (hasValidSignal(object, propertyName)) {
563 QDeclarativeExpression *declarativeExpression = new QDeclarativeExpression(context, object, expression.toString());
564 QDeclarativePropertyPrivate::setSignalExpression(property, declarativeExpression);
565 declarativeExpression->setSourceLocation(filename, line, column);
566 } else if (property.isProperty()) {
567 QDeclarativeBinding *binding = new QDeclarativeBinding(expression.toString(), object, context);
568 binding->setTarget(property);
569 binding->setSourceLocation(filename, line, column);
570 binding->setNotifyOnValueChanged(true);
571 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::setBinding(property, binding);
573 oldBinding->destroy();
576 qWarning() << "QDeclarativeEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object;
581 // not a valid property
583 if (m_statesDelegate)
584 ok = m_statesDelegate->setBindingForInvalidProperty(object, propertyName, expression, isLiteralValue);
586 qWarning() << "QDeclarativeEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object;
591 void QDeclarativeEngineDebugService::resetBinding(int objectId, const QString &propertyName)
593 QObject *object = objectForId(objectId);
594 QDeclarativeContext *context = qmlContext(object);
596 if (object && context) {
597 if (object->property(propertyName.toLatin1()).isValid()) {
598 QDeclarativeProperty property(object, propertyName);
599 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(property);
601 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::setBinding(property, 0);
603 oldBinding->destroy();
605 if (property.isResettable()) {
606 // Note: this will reset the property in any case, without regard to states
607 // Right now almost no QDeclarativeItem has reset methods for its properties (with the
608 // notable exception of QDeclarativeAnchors), so this is not a big issue
609 // later on, setBinding does take states into account
612 // overwrite with default value
613 if (QDeclarativeType *objType = QDeclarativeMetaType::qmlType(object->metaObject())) {
614 if (QObject *emptyObject = objType->create()) {
615 if (emptyObject->property(propertyName.toLatin1()).isValid()) {
616 QVariant defaultValue = QDeclarativeProperty(emptyObject, propertyName).read();
617 if (defaultValue.isValid()) {
618 setBinding(objectId, propertyName, defaultValue, true);
625 } else if (hasValidSignal(object, propertyName)) {
626 QDeclarativeProperty property(object, propertyName, context);
627 QDeclarativePropertyPrivate::setSignalExpression(property, 0);
629 if (m_statesDelegate)
630 m_statesDelegate->resetBindingForInvalidProperty(object, propertyName);
635 void QDeclarativeEngineDebugService::setMethodBody(int objectId, const QString &method, const QString &body)
637 QObject *object = objectForId(objectId);
638 QDeclarativeContext *context = qmlContext(object);
639 if (!object || !context || !context->engine())
641 QDeclarativeContextData *contextData = QDeclarativeContextData::get(context);
645 QDeclarativePropertyData dummy;
646 QDeclarativePropertyData *prop =
647 QDeclarativePropertyCache::property(context->engine(), object, method, dummy);
649 if (!prop || !prop->isVMEFunction())
652 QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex);
653 QList<QByteArray> paramNames = metaMethod.parameterNames();
656 for (int ii = 0; ii < paramNames.count(); ++ii) {
657 if (ii != 0) paramStr.append(QLatin1String(","));
658 paramStr.append(QString::fromUtf8(paramNames.at(ii)));
661 QString jsfunction = QLatin1String("(function ") + method + QLatin1String("(") + paramStr +
662 QLatin1String(") {");
664 jsfunction += QLatin1String("\n})");
666 QDeclarativeVMEMetaObject *vmeMetaObject =
667 static_cast<QDeclarativeVMEMetaObject*>(QObjectPrivate::get(object)->metaObject);
668 Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this
670 int lineNumber = vmeMetaObject->vmeMethodLineNumber(prop->coreIndex);
671 vmeMetaObject->setVmeMethod(prop->coreIndex, QDeclarativeExpressionPrivate::evalFunction(contextData, object, jsfunction, contextData->url.toString(), lineNumber));
674 void QDeclarativeEngineDebugService::propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value)
677 QDataStream rs(&reply, QIODevice::WriteOnly);
679 rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value);
684 void QDeclarativeEngineDebugService::addEngine(QDeclarativeEngine *engine)
687 Q_ASSERT(!m_engines.contains(engine));
689 m_engines.append(engine);
692 void QDeclarativeEngineDebugService::remEngine(QDeclarativeEngine *engine)
695 Q_ASSERT(m_engines.contains(engine));
697 m_engines.removeAll(engine);
700 void QDeclarativeEngineDebugService::objectCreated(QDeclarativeEngine *engine, QObject *object)
703 Q_ASSERT(m_engines.contains(engine));
705 int engineId = QDeclarativeDebugService::idForObject(engine);
706 int objectId = QDeclarativeDebugService::idForObject(object);
709 QDataStream rs(&reply, QIODevice::WriteOnly);
711 rs << QByteArray("OBJECT_CREATED") << engineId << objectId;
715 void QDeclarativeEngineDebugService::setStatesDelegate(QDeclarativeDebugStatesDelegate *delegate)
717 m_statesDelegate = delegate;