b824bc0e23293e35544b903a44716e46fbb111bf
[profile/ivi/qtdeclarative.git] / src / qml / debugger / qqmlenginedebugservice.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qqmlenginedebugservice_p.h"
43
44 #include "qqmldebugstatesdelegate_p.h"
45 #include <private/qqmlboundsignal_p.h>
46 #include <qqmlengine.h>
47 #include <private/qqmlmetatype_p.h>
48 #include <qqmlproperty.h>
49 #include <private/qqmlproperty_p.h>
50 #include <private/qqmlbinding_p.h>
51 #include <private/qqmlcontext_p.h>
52 #include <private/qqmlwatcher_p.h>
53 #include <private/qqmlvaluetype_p.h>
54 #include <private/qqmlvmemetaobject_p.h>
55 #include <private/qqmlexpression_p.h>
56
57 #include <QtCore/qdebug.h>
58 #include <QtCore/qmetaobject.h>
59
60 QT_BEGIN_NAMESPACE
61
62 Q_GLOBAL_STATIC(QQmlEngineDebugService, qmlEngineDebugService)
63
64 QQmlEngineDebugService *QQmlEngineDebugService::instance()
65 {
66     return qmlEngineDebugService();
67 }
68
69 QQmlEngineDebugService::QQmlEngineDebugService(QObject *parent)
70     : QQmlDebugService(QStringLiteral("QmlDebugger"), 2, parent),
71       m_watch(new QQmlWatcher(this)),
72       m_statesDelegate(0)
73 {
74     QObject::connect(m_watch, SIGNAL(propertyChanged(int,int,QMetaProperty,QVariant)),
75                      this, SLOT(propertyChanged(int,int,QMetaProperty,QVariant)));
76
77     registerService();
78 }
79
80 QQmlEngineDebugService::~QQmlEngineDebugService()
81 {
82     delete m_statesDelegate;
83 }
84
85 QDataStream &operator<<(QDataStream &ds, 
86                         const QQmlEngineDebugService::QQmlObjectData &data)
87 {
88     ds << data.url << data.lineNumber << data.columnNumber << data.idString
89        << data.objectName << data.objectType << data.objectId << data.contextId
90        << data.parentId;
91     return ds;
92 }
93
94 QDataStream &operator>>(QDataStream &ds, 
95                         QQmlEngineDebugService::QQmlObjectData &data)
96 {
97     ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString
98        >> data.objectName >> data.objectType >> data.objectId >> data.contextId
99        >> data.parentId;
100     return ds;
101 }
102
103 QDataStream &operator<<(QDataStream &ds, 
104                         const QQmlEngineDebugService::QQmlObjectProperty &data)
105 {
106     ds << (int)data.type << data.name << data.value << data.valueTypeName
107        << data.binding << data.hasNotifySignal;
108     return ds;
109 }
110
111 QDataStream &operator>>(QDataStream &ds,  
112                         QQmlEngineDebugService::QQmlObjectProperty &data)
113 {
114     int type;
115     ds >> type >> data.name >> data.value >> data.valueTypeName
116        >> data.binding >> data.hasNotifySignal;
117     data.type = (QQmlEngineDebugService::QQmlObjectProperty::Type)type;
118     return ds;
119 }
120
121 static inline bool isSignalPropertyName(const QString &signalName)
122 {
123     // see QmlCompiler::isSignalPropertyName
124     return signalName.length() >= 3 && signalName.startsWith(QLatin1String("on")) &&
125             signalName.at(2).isLetter() && signalName.at(2).isUpper();
126 }
127
128 static bool hasValidSignal(QObject *object, const QString &propertyName)
129 {
130     if (!isSignalPropertyName(propertyName))
131         return false;
132
133     QString signalName = propertyName.mid(2);
134     signalName[0] = signalName.at(0).toLower();
135
136     int sigIdx = QQmlPropertyPrivate::findSignalByName(object->metaObject(), signalName.toLatin1()).methodIndex();
137
138     if (sigIdx == -1)
139         return false;
140
141     return true;
142 }
143
144 QQmlEngineDebugService::QQmlObjectProperty
145 QQmlEngineDebugService::propertyData(QObject *obj, int propIdx)
146 {
147     QQmlObjectProperty rv;
148
149     QMetaProperty prop = obj->metaObject()->property(propIdx);
150
151     rv.type = QQmlObjectProperty::Unknown;
152     rv.valueTypeName = QString::fromUtf8(prop.typeName());
153     rv.name = QString::fromUtf8(prop.name());
154     rv.hasNotifySignal = prop.hasNotifySignal();
155     QQmlAbstractBinding *binding =
156             QQmlPropertyPrivate::binding(QQmlProperty(obj, rv.name));
157     if (binding)
158         rv.binding = binding->expression();
159
160     if (QQmlValueTypeFactory::isValueType(prop.userType())) {
161         rv.type = QQmlObjectProperty::Basic;
162     } else if (QQmlMetaType::isQObject(prop.userType()))  {
163         rv.type = QQmlObjectProperty::Object;
164     } else if (QQmlMetaType::isList(prop.userType())) {
165         rv.type = QQmlObjectProperty::List;
166     } else if (prop.userType() == QMetaType::QVariant) {
167         rv.type = QQmlObjectProperty::Variant;
168     }
169
170     QVariant value;
171     if (rv.type != QQmlObjectProperty::Unknown && prop.userType() != 0) {
172         value = prop.read(obj);
173     }
174     rv.value = valueContents(value);
175
176     return rv;
177 }
178
179 QVariant QQmlEngineDebugService::valueContents(const QVariant &value) const
180 {
181     int userType = value.userType();
182
183     //QObject * is not streamable.
184     //Convert all such instances to a String value
185
186     if (value.type() == QVariant::List) {
187         QVariantList contents;
188         QVariantList list = value.toList();
189         int count = list.size();
190         for (int i = 0; i < count; i++)
191             contents << valueContents(list.at(i));
192         return contents;
193     }
194
195     if (value.type() == QVariant::Map) {
196         QVariantMap contents;
197         QMapIterator<QString, QVariant> i(value.toMap());
198          while (i.hasNext()) {
199              i.next();
200              contents.insert(i.key(), valueContents(i.value()));
201          }
202         return contents;
203     }
204
205     if (QQmlValueTypeFactory::isValueType(userType))
206         return value;
207
208     if (QQmlMetaType::isQObject(userType)) {
209         QObject *o = QQmlMetaType::toQObject(value);
210         if (o) {
211             QString name = o->objectName();
212             if (name.isEmpty())
213                 name = QStringLiteral("<unnamed object>");
214             return name;
215         }
216     }
217
218     return QString(QStringLiteral("<unknown value>"));
219 }
220
221 void QQmlEngineDebugService::buildObjectDump(QDataStream &message, 
222                                                      QObject *object, bool recur, bool dumpProperties)
223 {
224     message << objectData(object);
225
226     QObjectList children = object->children();
227     
228     int childrenCount = children.count();
229     for (int ii = 0; ii < children.count(); ++ii) {
230         if (qobject_cast<QQmlContext*>(children[ii]))
231             --childrenCount;
232     }
233
234     message << childrenCount << recur;
235
236     QList<QQmlObjectProperty> fakeProperties;
237
238     for (int ii = 0; ii < children.count(); ++ii) {
239         QObject *child = children.at(ii);
240         if (qobject_cast<QQmlContext*>(child))
241             continue;
242          if (recur)
243              buildObjectDump(message, child, recur, dumpProperties);
244          else
245              message << objectData(child);
246     }
247
248     if (!dumpProperties) {
249         message << 0;
250         return;
251     }
252
253     QList<int> propertyIndexes;
254     for (int ii = 0; ii < object->metaObject()->propertyCount(); ++ii) {
255         if (object->metaObject()->property(ii).isScriptable())
256             propertyIndexes << ii;
257     }
258
259     QQmlData *ddata = QQmlData::get(object);
260     if (ddata && ddata->signalHandlers) {
261         QQmlAbstractBoundSignal *signalHandler = ddata->signalHandlers;
262
263         while (signalHandler) {
264             if (!dumpProperties) {
265                 signalHandler = signalHandler->m_nextSignal;
266                 continue;
267             }
268             QQmlObjectProperty prop;
269             prop.type = QQmlObjectProperty::SignalProperty;
270             prop.hasNotifySignal = false;
271             QQmlBoundSignalExpression *expr = signalHandler->expression();
272             if (expr) {
273                 prop.value = expr->expression();
274                 QObject *scope = expr->scopeObject();
275                 if (scope) {
276                     QString methodName = QString::fromLatin1(scope->metaObject()->method(signalHandler->index()).name());
277                     if (!methodName.isEmpty()) {
278                         prop.name = QLatin1String("on") + methodName[0].toUpper()
279                                 + methodName.mid(1);
280                     }
281                 }
282             }
283             fakeProperties << prop;
284
285             signalHandler = signalHandler->m_nextSignal;
286         }
287     }
288
289     message << propertyIndexes.size() + fakeProperties.count();
290
291     for (int ii = 0; ii < propertyIndexes.size(); ++ii)
292         message << propertyData(object, propertyIndexes.at(ii));
293
294     for (int ii = 0; ii < fakeProperties.count(); ++ii)
295         message << fakeProperties[ii];
296 }
297
298 void QQmlEngineDebugService::prepareDeferredObjects(QObject *obj)
299 {
300     qmlExecuteDeferred(obj);
301
302     QObjectList children = obj->children();
303     for (int ii = 0; ii < children.count(); ++ii) {
304         QObject *child = children.at(ii);
305         prepareDeferredObjects(child);
306     }
307
308 }
309
310 void QQmlEngineDebugService::storeObjectIds(QObject *co)
311 {
312     QQmlDebugService::idForObject(co);
313     QObjectList children = co->children();
314     for (int ii = 0; ii < children.count(); ++ii)
315         storeObjectIds(children.at(ii));
316 }
317
318 void QQmlEngineDebugService::buildObjectList(QDataStream &message,
319                                              QQmlContext *ctxt,
320                                              const QList<QPointer<QObject> > &instances)
321 {
322     QQmlContextData *p = QQmlContextData::get(ctxt);
323
324     QString ctxtName = ctxt->objectName();
325     int ctxtId = QQmlDebugService::idForObject(ctxt);
326     if (ctxt->contextObject())
327         storeObjectIds(ctxt->contextObject());
328
329     message << ctxtName << ctxtId;
330
331     int count = 0;
332
333     QQmlContextData *child = p->childContexts;
334     while (child) {
335         ++count;
336         child = child->nextChild;
337     }
338
339     message << count;
340
341     child = p->childContexts;
342     while (child) {
343         buildObjectList(message, child->asQQmlContext(), instances);
344         child = child->nextChild;
345     }
346
347     count = 0;
348     for (int ii = 0; ii < instances.count(); ++ii) {
349         QQmlData *data = QQmlData::get(instances.at(ii));
350         if (data->context == p)
351             count ++;
352     }
353     message << count;
354
355     for (int ii = 0; ii < instances.count(); ++ii) {
356         QQmlData *data = QQmlData::get(instances.at(ii));
357         if (data->context == p)
358             message << objectData(instances.at(ii));
359     }
360 }
361
362 void QQmlEngineDebugService::buildStatesList(bool cleanList,
363                                              const QList<QPointer<QObject> > &instances)
364 {
365     if (m_statesDelegate)
366         m_statesDelegate->buildStatesList(cleanList, instances);
367 }
368
369 QQmlEngineDebugService::QQmlObjectData
370 QQmlEngineDebugService::objectData(QObject *object)
371 {
372     QQmlData *ddata = QQmlData::get(object);
373     QQmlObjectData rv;
374     if (ddata && ddata->outerContext) {
375         rv.url = ddata->outerContext->url;
376         rv.lineNumber = ddata->lineNumber;
377         rv.columnNumber = ddata->columnNumber;
378     } else {
379         rv.lineNumber = -1;
380         rv.columnNumber = -1;
381     }
382
383     QQmlContext *context = qmlContext(object);
384     if (context) {
385         QQmlContextData *cdata = QQmlContextData::get(context);
386         if (cdata)
387             rv.idString = cdata->findObjectId(object);
388     }
389
390     rv.objectName = object->objectName();
391     rv.objectId = QQmlDebugService::idForObject(object);
392     rv.contextId = QQmlDebugService::idForObject(qmlContext(object));
393     rv.parentId = QQmlDebugService::idForObject(object->parent());
394     QQmlType *type = QQmlMetaType::qmlType(object->metaObject());
395     if (type) {
396         QString typeName = type->qmlTypeName();
397         int lastSlash = typeName.lastIndexOf(QLatin1Char('/'));
398         rv.objectType = lastSlash < 0 ? typeName : typeName.mid(lastSlash+1);
399     } else {
400         rv.objectType = QString::fromUtf8(object->metaObject()->className());
401         int marker = rv.objectType.indexOf(QLatin1String("_QMLTYPE_"));
402         if (marker != -1)
403             rv.objectType = rv.objectType.left(marker);
404     }
405
406     return rv;
407 }
408
409 void QQmlEngineDebugService::messageReceived(const QByteArray &message)
410 {
411     QMetaObject::invokeMethod(this, "processMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message));
412 }
413
414 void QQmlEngineDebugService::processMessage(const QByteArray &message)
415 {
416     QQmlDebugStream ds(message);
417
418     QByteArray type;
419     int queryId;
420     ds >> type >> queryId;
421
422     QByteArray reply;
423     QQmlDebugStream rs(&reply, QIODevice::WriteOnly);
424
425     if (type == "LIST_ENGINES") {
426         rs << QByteArray("LIST_ENGINES_R");
427         rs << queryId << m_engines.count();
428
429         for (int ii = 0; ii < m_engines.count(); ++ii) {
430             QQmlEngine *engine = m_engines.at(ii);
431
432             QString engineName = engine->objectName();
433             int engineId = QQmlDebugService::idForObject(engine);
434
435             rs << engineName << engineId;
436         }
437
438     } else if (type == "LIST_OBJECTS") {
439         int engineId = -1;
440         ds >> engineId;
441
442         QQmlEngine *engine =
443                 qobject_cast<QQmlEngine *>(QQmlDebugService::objectForId(engineId));
444
445         rs << QByteArray("LIST_OBJECTS_R") << queryId;
446
447         if (engine) {
448             QQmlContext *rootContext = engine->rootContext();
449             // Clean deleted objects
450             QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(rootContext);
451             for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) {
452                 if (!ctxtPriv->instances.at(ii)) {
453                     ctxtPriv->instances.removeAt(ii);
454                     --ii;
455                 }
456             }
457             buildObjectList(rs, rootContext, ctxtPriv->instances);
458             buildStatesList(true, ctxtPriv->instances);
459         }
460
461     } else if (type == "FETCH_OBJECT") {
462         int objectId;
463         bool recurse;
464         bool dumpProperties = true;
465
466         ds >> objectId >> recurse >> dumpProperties;
467
468         QObject *object = QQmlDebugService::objectForId(objectId);
469
470         rs << QByteArray("FETCH_OBJECT_R") << queryId;
471
472         if (object) {
473             if (recurse)
474                 prepareDeferredObjects(object);
475             buildObjectDump(rs, object, recurse, dumpProperties);
476         }
477
478     } else if (type == "FETCH_OBJECTS_FOR_LOCATION") {
479         QString file;
480         int lineNumber;
481         int columnNumber;
482         bool recurse;
483         bool dumpProperties = true;
484
485         ds >> file >> lineNumber >> columnNumber >> recurse >> dumpProperties;
486
487         QList<QObject*> objects = QQmlDebugService::objectForLocationInfo(
488                                 file, lineNumber, columnNumber);
489
490         rs << QByteArray("FETCH_OBJECTS_FOR_LOCATION_R") << queryId
491            << objects.count();
492
493         foreach (QObject *object, objects) {
494             if (recurse)
495                 prepareDeferredObjects(object);
496             buildObjectDump(rs, object, recurse, dumpProperties);
497         }
498
499     } else if (type == "WATCH_OBJECT") {
500         int objectId;
501
502         ds >> objectId;
503         bool ok = m_watch->addWatch(queryId, objectId);
504
505         rs << QByteArray("WATCH_OBJECT_R") << queryId << ok;
506
507     } else if (type == "WATCH_PROPERTY") {
508         int objectId;
509         QByteArray property;
510
511         ds >> objectId >> property;
512         bool ok = m_watch->addWatch(queryId, objectId, property);
513
514         rs << QByteArray("WATCH_PROPERTY_R") << queryId << ok;
515
516     } else if (type == "WATCH_EXPR_OBJECT") {
517         int debugId;
518         QString expr;
519
520         ds >> debugId >> expr;
521         bool ok = m_watch->addWatch(queryId, debugId, expr);
522
523         rs << QByteArray("WATCH_EXPR_OBJECT_R") << queryId << ok;
524
525     } else if (type == "NO_WATCH") {
526         bool ok = m_watch->removeWatch(queryId);
527
528         rs << QByteArray("NO_WATCH_R") << queryId << ok;
529
530     } else if (type == "EVAL_EXPRESSION") {
531         int objectId;
532         QString expr;
533
534         ds >> objectId >> expr;
535         int engineId = -1;
536         if (!ds.atEnd())
537             ds >> engineId;
538
539         QObject *object = QQmlDebugService::objectForId(objectId);
540         QQmlContext *context = qmlContext(object);
541         if (!context) {
542             QQmlEngine *engine = qobject_cast<QQmlEngine *>(
543                         QQmlDebugService::objectForId(engineId));
544             if (engine && m_engines.contains(engine))
545                 context = engine->rootContext();
546         }
547         QVariant result;
548         if (context) {
549             QQmlExpression exprObj(context, object, expr);
550             bool undefined = false;
551             QVariant value = exprObj.evaluate(&undefined);
552             if (undefined)
553                 result = QString(QStringLiteral("<undefined>"));
554             else
555                 result = valueContents(value);
556         } else {
557             result = QString(QStringLiteral("<unknown context>"));
558         }
559
560         rs << QByteArray("EVAL_EXPRESSION_R") << queryId << result;
561
562     } else if (type == "SET_BINDING") {
563         int objectId;
564         QString propertyName;
565         QVariant expr;
566         bool isLiteralValue;
567         QString filename;
568         int line;
569         ds >> objectId >> propertyName >> expr >> isLiteralValue >>
570               filename >> line;
571         bool ok = setBinding(objectId, propertyName, expr, isLiteralValue,
572                              filename, line);
573
574         rs << QByteArray("SET_BINDING_R") << queryId << ok;
575
576     } else if (type == "RESET_BINDING") {
577         int objectId;
578         QString propertyName;
579         ds >> objectId >> propertyName;
580         bool ok = resetBinding(objectId, propertyName);
581
582         rs << QByteArray("RESET_BINDING_R") << queryId << ok;
583
584     } else if (type == "SET_METHOD_BODY") {
585         int objectId;
586         QString methodName;
587         QString methodBody;
588         ds >> objectId >> methodName >> methodBody;
589         bool ok = setMethodBody(objectId, methodName, methodBody);
590
591         rs << QByteArray("SET_METHOD_BODY_R") << queryId << ok;
592
593     }
594     sendMessage(reply);
595 }
596
597 bool QQmlEngineDebugService::setBinding(int objectId,
598                                                 const QString &propertyName,
599                                                 const QVariant &expression,
600                                                 bool isLiteralValue,
601                                                 QString filename,
602                                                 int line,
603                                                 int column)
604 {
605     bool ok = true;
606     QObject *object = objectForId(objectId);
607     QQmlContext *context = qmlContext(object);
608
609     if (object && context) {
610         QQmlProperty property(object, propertyName, context);
611         if (property.isValid()) {
612
613             bool inBaseState = true;
614             if (m_statesDelegate) {
615                 m_statesDelegate->updateBinding(context, property, expression, isLiteralValue,
616                                                 filename, line, column, &inBaseState);
617             }
618
619             if (inBaseState) {
620                 if (isLiteralValue) {
621                     property.write(expression);
622                 } else if (hasValidSignal(object, propertyName)) {
623                     QQmlBoundSignalExpression *qmlExpression = new QQmlBoundSignalExpression(QQmlContextData::get(context), object, expression.toString(),
624                                                                                              false, filename, line, column);
625                     QQmlPropertyPrivate::takeSignalExpression(property, qmlExpression);
626                 } else if (property.isProperty()) {
627                     QQmlBinding *binding = new QQmlBinding(expression.toString(), false, object, QQmlContextData::get(context), filename, line, column);;
628                     binding->setTarget(property);
629                     QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::setBinding(property, binding);
630                     if (oldBinding)
631                         oldBinding->destroy();
632                     binding->update();
633                 } else {
634                     ok = false;
635                     qWarning() << "QQmlEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object;
636                 }
637             }
638
639         } else {
640             // not a valid property
641             if (m_statesDelegate)
642                 ok = m_statesDelegate->setBindingForInvalidProperty(object, propertyName, expression, isLiteralValue);
643             if (!ok)
644                 qWarning() << "QQmlEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object;
645         }
646     }
647     return ok;
648 }
649
650 bool QQmlEngineDebugService::resetBinding(int objectId, const QString &propertyName)
651 {
652     QObject *object = objectForId(objectId);
653     QQmlContext *context = qmlContext(object);
654
655     if (object && context) {
656         QString parentProperty = propertyName;
657         if (propertyName.indexOf(QLatin1Char('.')) != -1)
658             parentProperty = propertyName.left(propertyName.indexOf(QLatin1Char('.')));
659
660         if (object->property(parentProperty.toLatin1()).isValid()) {
661             QQmlProperty property(object, propertyName);
662             QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(property);
663             if (oldBinding) {
664                 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::setBinding(property, 0);
665                 if (oldBinding)
666                     oldBinding->destroy();
667             }
668             if (property.isResettable()) {
669                 // Note: this will reset the property in any case, without regard to states
670                 // Right now almost no QQuickItem has reset methods for its properties (with the
671                 // notable exception of QQuickAnchors), so this is not a big issue
672                 // later on, setBinding does take states into account
673                 property.reset();
674             } else {
675                 // overwrite with default value
676                 if (QQmlType *objType = QQmlMetaType::qmlType(object->metaObject())) {
677                     if (QObject *emptyObject = objType->create()) {
678                         if (emptyObject->property(parentProperty.toLatin1()).isValid()) {
679                             QVariant defaultValue = QQmlProperty(emptyObject, propertyName).read();
680                             if (defaultValue.isValid()) {
681                                 setBinding(objectId, propertyName, defaultValue, true);
682                             }
683                         }
684                         delete emptyObject;
685                     }
686                 }
687             }
688             return true;
689         }
690
691         if (hasValidSignal(object, propertyName)) {
692             QQmlProperty property(object, propertyName, context);
693             QQmlPropertyPrivate::setSignalExpression(property, 0);
694             return true;
695         }
696
697         if (m_statesDelegate) {
698             m_statesDelegate->resetBindingForInvalidProperty(object, propertyName);
699             return true;
700         }
701
702         return false;
703     }
704     // object or context null.
705     return false;
706 }
707
708 bool QQmlEngineDebugService::setMethodBody(int objectId, const QString &method, const QString &body)
709 {
710     QObject *object = objectForId(objectId);
711     QQmlContext *context = qmlContext(object);
712     if (!object || !context || !context->engine())
713         return false;
714     QQmlContextData *contextData = QQmlContextData::get(context);
715     if (!contextData)
716         return false;
717
718     QQmlPropertyData dummy;
719     QQmlPropertyData *prop =
720             QQmlPropertyCache::property(context->engine(), object, method, dummy);
721
722     if (!prop || !prop->isVMEFunction())
723         return false;
724
725     QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex);
726     QList<QByteArray> paramNames = metaMethod.parameterNames();
727
728     QString paramStr;
729     for (int ii = 0; ii < paramNames.count(); ++ii) {
730         if (ii != 0) paramStr.append(QLatin1Char(','));
731         paramStr.append(QString::fromUtf8(paramNames.at(ii)));
732     }
733
734     QString jsfunction = QLatin1String("(function ") + method + QLatin1Char('(') + paramStr +
735             QLatin1String(") {");
736     jsfunction += body;
737     jsfunction += QLatin1String("\n})");
738
739     QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(object);
740     Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this
741
742     int lineNumber = vmeMetaObject->vmeMethodLineNumber(prop->coreIndex);
743     vmeMetaObject->setVmeMethod(prop->coreIndex, QQmlExpressionPrivate::evalFunction(contextData, object, jsfunction, contextData->url.toString(), lineNumber));
744     return true;
745 }
746
747 void QQmlEngineDebugService::propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value)
748 {
749     QByteArray reply;
750     QQmlDebugStream rs(&reply, QIODevice::WriteOnly);
751
752     rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value);
753
754     sendMessage(reply);
755 }
756
757 void QQmlEngineDebugService::addEngine(QQmlEngine *engine)
758 {
759     Q_ASSERT(engine);
760     Q_ASSERT(!m_engines.contains(engine));
761
762     m_engines.append(engine);
763 }
764
765 void QQmlEngineDebugService::remEngine(QQmlEngine *engine)
766 {
767     Q_ASSERT(engine);
768     Q_ASSERT(m_engines.contains(engine));
769
770     m_engines.removeAll(engine);
771 }
772
773 void QQmlEngineDebugService::objectCreated(QQmlEngine *engine, QObject *object)
774 {
775     Q_ASSERT(engine);
776     Q_ASSERT(m_engines.contains(engine));
777
778     int engineId = QQmlDebugService::idForObject(engine);
779     int objectId = QQmlDebugService::idForObject(object);
780     int parentId = QQmlDebugService::idForObject(object->parent());
781
782     QByteArray reply;
783     QQmlDebugStream rs(&reply, QIODevice::WriteOnly);
784
785     //unique queryId -1
786     rs << QByteArray("OBJECT_CREATED") << -1 << engineId << objectId << parentId;
787     sendMessage(reply);
788 }
789
790 void QQmlEngineDebugService::setStatesDelegate(QQmlDebugStatesDelegate *delegate)
791 {
792     m_statesDelegate = delegate;
793 }
794
795 QT_END_NAMESPACE