1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
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 "private/qdeclarativeenginedebugservice_p.h"
44 #include "private/qdeclarativeboundsignal_p.h"
45 #include "qdeclarativeengine.h"
46 #include "private/qdeclarativemetatype_p.h"
47 #include "qdeclarativeproperty.h"
48 #include "private/qdeclarativeproperty_p.h"
49 #include "private/qdeclarativebinding_p.h"
50 #include "private/qdeclarativecontext_p.h"
51 #include "private/qdeclarativewatcher_p.h"
52 #include "private/qdeclarativevaluetype_p.h"
53 #include "private/qdeclarativevmemetaobject_p.h"
54 #include "private/qdeclarativeexpression_p.h"
55 #include "private/qdeclarativepropertychanges_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"), parent),
71 m_watch(new QDeclarativeWatcher(this))
73 QObject::connect(m_watch, SIGNAL(propertyChanged(int,int,QMetaProperty,QVariant)),
74 this, SLOT(propertyChanged(int,int,QMetaProperty,QVariant)));
77 QDataStream &operator<<(QDataStream &ds,
78 const QDeclarativeEngineDebugService::QDeclarativeObjectData &data)
80 ds << data.url << data.lineNumber << data.columnNumber << data.idString
81 << data.objectName << data.objectType << data.objectId << data.contextId;
85 QDataStream &operator>>(QDataStream &ds,
86 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 const QDeclarativeEngineDebugService::QDeclarativeObjectProperty &data)
96 ds << (int)data.type << data.name << data.value << data.valueTypeName
97 << data.binding << data.hasNotifySignal;
101 QDataStream &operator>>(QDataStream &ds,
102 QDeclarativeEngineDebugService::QDeclarativeObjectProperty &data)
105 ds >> type >> data.name >> data.value >> data.valueTypeName
106 >> data.binding >> data.hasNotifySignal;
107 data.type = (QDeclarativeEngineDebugService::QDeclarativeObjectProperty::Type)type;
111 static inline bool isSignalPropertyName(const QString &signalName)
113 // see QmlCompiler::isSignalPropertyName
114 return signalName.length() >= 3 && signalName.startsWith(QLatin1String("on")) &&
115 signalName.at(2).isLetter() && signalName.at(2).isUpper();
118 static bool hasValidSignal(QObject *object, const QString &propertyName)
120 if (!isSignalPropertyName(propertyName))
123 QString signalName = propertyName.mid(2);
124 signalName[0] = signalName.at(0).toLower();
126 int sigIdx = QDeclarativePropertyPrivate::findSignalByName(object->metaObject(), signalName.toLatin1()).methodIndex();
134 QDeclarativeEngineDebugService::QDeclarativeObjectProperty
135 QDeclarativeEngineDebugService::propertyData(QObject *obj, int propIdx)
137 QDeclarativeObjectProperty rv;
139 QMetaProperty prop = obj->metaObject()->property(propIdx);
141 rv.type = QDeclarativeObjectProperty::Unknown;
142 rv.valueTypeName = QString::fromUtf8(prop.typeName());
143 rv.name = QString::fromUtf8(prop.name());
144 rv.hasNotifySignal = prop.hasNotifySignal();
145 QDeclarativeAbstractBinding *binding =
146 QDeclarativePropertyPrivate::binding(QDeclarativeProperty(obj, rv.name));
148 rv.binding = binding->expression();
150 if (QDeclarativeValueTypeFactory::isValueType(prop.userType())) {
151 rv.type = QDeclarativeObjectProperty::Basic;
152 } else if (QDeclarativeMetaType::isQObject(prop.userType())) {
153 rv.type = QDeclarativeObjectProperty::Object;
154 } else if (QDeclarativeMetaType::isList(prop.userType())) {
155 rv.type = QDeclarativeObjectProperty::List;
159 if (rv.type != QDeclarativeObjectProperty::Unknown && prop.userType() != 0) {
160 value = prop.read(obj);
162 rv.value = valueContents(value);
167 QVariant QDeclarativeEngineDebugService::valueContents(const QVariant &value) const
169 int userType = value.userType();
171 if (value.type() == QVariant::List) {
172 QVariantList contents;
173 QVariantList list = value.toList();
174 int count = list.size();
175 for (int i = 0; i < count; i++)
176 contents << valueContents(list.at(i));
180 if (QDeclarativeValueTypeFactory::isValueType(userType))
183 if (QDeclarativeMetaType::isQObject(userType)) {
184 QObject *o = QDeclarativeMetaType::toQObject(value);
186 QString name = o->objectName();
188 name = QLatin1String("<unnamed object>");
193 return QLatin1String("<unknown value>");
196 void QDeclarativeEngineDebugService::buildObjectDump(QDataStream &message,
197 QObject *object, bool recur, bool dumpProperties)
199 message << objectData(object);
201 QObjectList children = object->children();
203 int childrenCount = children.count();
204 for (int ii = 0; ii < children.count(); ++ii) {
205 if (qobject_cast<QDeclarativeContext*>(children[ii]) || QDeclarativeBoundSignal::cast(children[ii]))
209 message << childrenCount << recur;
211 QList<QDeclarativeObjectProperty> fakeProperties;
213 for (int ii = 0; ii < children.count(); ++ii) {
214 QObject *child = children.at(ii);
215 if (qobject_cast<QDeclarativeContext*>(child))
217 QDeclarativeBoundSignal *signal = QDeclarativeBoundSignal::cast(child);
221 QDeclarativeObjectProperty prop;
222 prop.type = QDeclarativeObjectProperty::SignalProperty;
223 prop.hasNotifySignal = false;
224 QDeclarativeExpression *expr = signal->expression();
226 prop.value = expr->expression();
227 QObject *scope = expr->scopeObject();
229 QString sig = QLatin1String(scope->metaObject()->method(signal->index()).signature());
230 int lparen = sig.indexOf(QLatin1Char('('));
232 QString methodName = sig.mid(0, lparen);
233 prop.name = QLatin1String("on") + methodName[0].toUpper()
238 fakeProperties << prop;
241 buildObjectDump(message, child, recur, dumpProperties);
243 message << objectData(child);
247 if (!dumpProperties) {
252 QList<int> propertyIndexes;
253 for (int ii = 0; ii < object->metaObject()->propertyCount(); ++ii) {
254 if (object->metaObject()->property(ii).isScriptable())
255 propertyIndexes << ii;
258 message << propertyIndexes.size() + fakeProperties.count();
260 for (int ii = 0; ii < propertyIndexes.size(); ++ii)
261 message << propertyData(object, propertyIndexes.at(ii));
263 for (int ii = 0; ii < fakeProperties.count(); ++ii)
264 message << fakeProperties[ii];
267 void QDeclarativeEngineDebugService::prepareDeferredObjects(QObject *obj)
269 qmlExecuteDeferred(obj);
271 QObjectList children = obj->children();
272 for (int ii = 0; ii < children.count(); ++ii) {
273 QObject *child = children.at(ii);
274 prepareDeferredObjects(child);
279 void QDeclarativeEngineDebugService::buildObjectList(QDataStream &message, QDeclarativeContext *ctxt)
281 QDeclarativeContextData *p = QDeclarativeContextData::get(ctxt);
283 QString ctxtName = ctxt->objectName();
284 int ctxtId = QDeclarativeDebugService::idForObject(ctxt);
286 message << ctxtName << ctxtId;
290 QDeclarativeContextData *child = p->childContexts;
293 child = child->nextChild;
298 child = p->childContexts;
300 buildObjectList(message, child->asQDeclarativeContext());
301 child = child->nextChild;
304 // Clean deleted objects
305 QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(ctxt);
306 for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) {
307 if (!ctxtPriv->instances.at(ii)) {
308 ctxtPriv->instances.removeAt(ii);
313 message << ctxtPriv->instances.count();
314 for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) {
315 message << objectData(ctxtPriv->instances.at(ii));
319 void QDeclarativeEngineDebugService::buildStatesList(QDeclarativeContext *ctxt, bool cleanList=false)
324 QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(ctxt);
325 for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) {
326 buildStatesList(ctxtPriv->instances.at(ii));
329 QDeclarativeContextData *child = QDeclarativeContextData::get(ctxt)->childContexts;
331 buildStatesList(child->asQDeclarativeContext());
332 child = child->nextChild;
336 void QDeclarativeEngineDebugService::buildStatesList(QObject *obj)
338 if (QDeclarativeState *state = qobject_cast<QDeclarativeState *>(obj)) {
339 m_allStates.append(state);
342 QObjectList children = obj->children();
343 for (int ii = 0; ii < children.count(); ++ii) {
344 buildStatesList(children.at(ii));
348 QDeclarativeEngineDebugService::QDeclarativeObjectData
349 QDeclarativeEngineDebugService::objectData(QObject *object)
351 QDeclarativeData *ddata = QDeclarativeData::get(object);
352 QDeclarativeObjectData rv;
353 if (ddata && ddata->outerContext) {
354 rv.url = ddata->outerContext->url;
355 rv.lineNumber = ddata->lineNumber;
356 rv.columnNumber = ddata->columnNumber;
359 rv.columnNumber = -1;
362 QDeclarativeContext *context = qmlContext(object);
364 QDeclarativeContextData *cdata = QDeclarativeContextData::get(context);
366 rv.idString = cdata->findObjectId(object);
369 rv.objectName = object->objectName();
370 rv.objectId = QDeclarativeDebugService::idForObject(object);
371 rv.contextId = QDeclarativeDebugService::idForObject(qmlContext(object));
373 QDeclarativeType *type = QDeclarativeMetaType::qmlType(object->metaObject());
375 QString typeName = QLatin1String(type->qmlTypeName());
376 int lastSlash = typeName.lastIndexOf(QLatin1Char('/'));
377 rv.objectType = lastSlash < 0 ? typeName : typeName.mid(lastSlash+1);
379 rv.objectType = QString::fromUtf8(object->metaObject()->className());
380 int marker = rv.objectType.indexOf(QLatin1String("_QMLTYPE_"));
382 rv.objectType = rv.objectType.left(marker);
388 void QDeclarativeEngineDebugService::messageReceived(const QByteArray &message)
390 QDataStream ds(message);
395 if (type == "LIST_ENGINES") {
400 QDataStream rs(&reply, QIODevice::WriteOnly);
401 rs << QByteArray("LIST_ENGINES_R");
402 rs << queryId << m_engines.count();
404 for (int ii = 0; ii < m_engines.count(); ++ii) {
405 QDeclarativeEngine *engine = m_engines.at(ii);
407 QString engineName = engine->objectName();
408 int engineId = QDeclarativeDebugService::idForObject(engine);
410 rs << engineName << engineId;
414 } else if (type == "LIST_OBJECTS") {
417 ds >> queryId >> engineId;
419 QDeclarativeEngine *engine =
420 qobject_cast<QDeclarativeEngine *>(QDeclarativeDebugService::objectForId(engineId));
423 QDataStream rs(&reply, QIODevice::WriteOnly);
424 rs << QByteArray("LIST_OBJECTS_R") << queryId;
427 buildObjectList(rs, engine->rootContext());
428 buildStatesList(engine->rootContext(), true);
432 } else if (type == "FETCH_OBJECT") {
436 bool dumpProperties = true;
438 ds >> queryId >> objectId >> recurse >> dumpProperties;
440 QObject *object = QDeclarativeDebugService::objectForId(objectId);
443 QDataStream rs(&reply, QIODevice::WriteOnly);
444 rs << QByteArray("FETCH_OBJECT_R") << queryId;
448 prepareDeferredObjects(object);
449 buildObjectDump(rs, object, recurse, dumpProperties);
453 } else if (type == "WATCH_OBJECT") {
457 ds >> queryId >> objectId;
458 bool ok = m_watch->addWatch(queryId, objectId);
461 QDataStream rs(&reply, QIODevice::WriteOnly);
462 rs << QByteArray("WATCH_OBJECT_R") << queryId << ok;
465 } else if (type == "WATCH_PROPERTY") {
470 ds >> queryId >> objectId >> property;
471 bool ok = m_watch->addWatch(queryId, objectId, property);
474 QDataStream rs(&reply, QIODevice::WriteOnly);
475 rs << QByteArray("WATCH_PROPERTY_R") << queryId << ok;
478 } else if (type == "WATCH_EXPR_OBJECT") {
483 ds >> queryId >> debugId >> expr;
484 bool ok = m_watch->addWatch(queryId, debugId, expr);
487 QDataStream rs(&reply, QIODevice::WriteOnly);
488 rs << QByteArray("WATCH_EXPR_OBJECT_R") << queryId << ok;
490 } else if (type == "NO_WATCH") {
494 m_watch->removeWatch(queryId);
495 } else if (type == "EVAL_EXPRESSION") {
500 ds >> queryId >> objectId >> expr;
502 QObject *object = QDeclarativeDebugService::objectForId(objectId);
503 QDeclarativeContext *context = qmlContext(object);
505 if (object && context) {
506 QDeclarativeExpression exprObj(context, object, expr);
507 bool undefined = false;
508 QVariant value = exprObj.evaluate(&undefined);
510 result = QLatin1String("<undefined>");
512 result = valueContents(value);
514 result = QLatin1String("<unknown context>");
518 QDataStream rs(&reply, QIODevice::WriteOnly);
519 rs << QByteArray("EVAL_EXPRESSION_R") << queryId << result;
522 } else if (type == "SET_BINDING") {
524 QString propertyName;
529 ds >> objectId >> propertyName >> expr >> isLiteralValue;
530 if (!ds.atEnd()) { // backward compatibility from 2.1, 2.2
531 ds >> filename >> line;
533 setBinding(objectId, propertyName, expr, isLiteralValue, filename, line);
534 } else if (type == "RESET_BINDING") {
536 QString propertyName;
537 ds >> objectId >> propertyName;
538 resetBinding(objectId, propertyName);
539 } else if (type == "SET_METHOD_BODY") {
543 ds >> objectId >> methodName >> methodBody;
544 setMethodBody(objectId, methodName, methodBody);
548 void QDeclarativeEngineDebugService::setBinding(int objectId,
549 const QString &propertyName,
550 const QVariant &expression,
555 QObject *object = objectForId(objectId);
556 QDeclarativeContext *context = qmlContext(object);
558 if (object && context) {
559 QDeclarativeProperty property(object, propertyName, context);
560 if (property.isValid()) {
562 bool inBaseState = true;
564 foreach(QWeakPointer<QDeclarativeState> statePointer, m_allStates) {
565 if (QDeclarativeState *state = statePointer.data()) {
566 // here we assume that the revert list on itself defines the base state
567 if (state->isStateActive() && state->containsPropertyInRevertList(object, propertyName)) {
570 QDeclarativeBinding *newBinding = 0;
571 if (!isLiteralValue) {
572 newBinding = new QDeclarativeBinding(expression.toString(), object, context);
573 newBinding->setTarget(property);
574 newBinding->setNotifyOnValueChanged(true);
575 newBinding->setSourceLocation(filename, line);
578 state->changeBindingInRevertList(object, propertyName, newBinding);
581 state->changeValueInRevertList(object, propertyName, expression);
587 if (isLiteralValue) {
588 property.write(expression);
589 } else if (hasValidSignal(object, propertyName)) {
590 QDeclarativeExpression *declarativeExpression = new QDeclarativeExpression(context, object, expression.toString());
591 QDeclarativePropertyPrivate::setSignalExpression(property, declarativeExpression);
592 declarativeExpression->setSourceLocation(filename, line);
593 } else if (property.isProperty()) {
594 QDeclarativeBinding *binding = new QDeclarativeBinding(expression.toString(), object, context);
595 binding->setTarget(property);
596 binding->setSourceLocation(filename, line);
597 binding->setNotifyOnValueChanged(true);
598 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::setBinding(property, binding);
600 oldBinding->destroy();
603 qWarning() << "QDeclarativeEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object;
608 // not a valid property
609 if (QDeclarativePropertyChanges *propertyChanges = qobject_cast<QDeclarativePropertyChanges *>(object)) {
610 if (isLiteralValue) {
611 propertyChanges->changeValue(propertyName, expression);
613 propertyChanges->changeExpression(propertyName, expression.toString());
616 qWarning() << "QDeclarativeEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object;
622 void QDeclarativeEngineDebugService::resetBinding(int objectId, const QString &propertyName)
624 QObject *object = objectForId(objectId);
625 QDeclarativeContext *context = qmlContext(object);
627 if (object && context) {
628 if (object->property(propertyName.toLatin1()).isValid()) {
629 QDeclarativeProperty property(object, propertyName);
630 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(property);
632 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::setBinding(property, 0);
634 oldBinding->destroy();
636 if (property.isResettable()) {
637 // Note: this will reset the property in any case, without regard to states
638 // Right now almost no QDeclarativeItem has reset methods for its properties (with the
639 // notable exception of QDeclarativeAnchors), so this is not a big issue
640 // later on, setBinding does take states into account
643 // overwrite with default value
644 if (QDeclarativeType *objType = QDeclarativeMetaType::qmlType(object->metaObject())) {
645 if (QObject *emptyObject = objType->create()) {
646 if (emptyObject->property(propertyName.toLatin1()).isValid()) {
647 QVariant defaultValue = QDeclarativeProperty(emptyObject, propertyName).read();
648 if (defaultValue.isValid()) {
649 setBinding(objectId, propertyName, defaultValue, true);
656 } else if (hasValidSignal(object, propertyName)) {
657 QDeclarativeProperty property(object, propertyName, context);
658 QDeclarativePropertyPrivate::setSignalExpression(property, 0);
660 if (QDeclarativePropertyChanges *propertyChanges = qobject_cast<QDeclarativePropertyChanges *>(object)) {
661 propertyChanges->removeProperty(propertyName);
667 void QDeclarativeEngineDebugService::setMethodBody(int objectId, const QString &method, const QString &body)
669 QObject *object = objectForId(objectId);
670 QDeclarativeContext *context = qmlContext(object);
671 if (!object || !context || !context->engine())
673 QDeclarativeContextData *contextData = QDeclarativeContextData::get(context);
677 QDeclarativePropertyCache::Data dummy;
678 QDeclarativePropertyCache::Data *prop =
679 QDeclarativePropertyCache::property(context->engine(), object, method, dummy);
681 if (!prop || !prop->isVMEFunction())
684 QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex);
685 QList<QByteArray> paramNames = metaMethod.parameterNames();
688 for (int ii = 0; ii < paramNames.count(); ++ii) {
689 if (ii != 0) paramStr.append(QLatin1String(","));
690 paramStr.append(QString::fromUtf8(paramNames.at(ii)));
693 QString jsfunction = QLatin1String("(function ") + method + QLatin1String("(") + paramStr +
694 QLatin1String(") {");
696 jsfunction += QLatin1String("\n})");
698 QDeclarativeVMEMetaObject *vmeMetaObject =
699 static_cast<QDeclarativeVMEMetaObject*>(QObjectPrivate::get(object)->metaObject);
700 Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this
702 int lineNumber = vmeMetaObject->vmeMethodLineNumber(prop->coreIndex);
703 vmeMetaObject->setVmeMethod(prop->coreIndex, QDeclarativeExpressionPrivate::evalFunction(contextData, object, jsfunction, contextData->url.toString(), lineNumber));
706 void QDeclarativeEngineDebugService::propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value)
709 QDataStream rs(&reply, QIODevice::WriteOnly);
711 rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value);
716 void QDeclarativeEngineDebugService::addEngine(QDeclarativeEngine *engine)
719 Q_ASSERT(!m_engines.contains(engine));
721 m_engines.append(engine);
724 void QDeclarativeEngineDebugService::remEngine(QDeclarativeEngine *engine)
727 Q_ASSERT(m_engines.contains(engine));
729 m_engines.removeAll(engine);
732 void QDeclarativeEngineDebugService::objectCreated(QDeclarativeEngine *engine, QObject *object)
735 Q_ASSERT(m_engines.contains(engine));
737 int engineId = QDeclarativeDebugService::idForObject(engine);
738 int objectId = QDeclarativeDebugService::idForObject(object);
741 QDataStream rs(&reply, QIODevice::WriteOnly);
743 rs << QByteArray("OBJECT_CREATED") << engineId << objectId;