Debugger: Move server into it's own thread
[profile/ivi/qtdeclarative.git] / src / declarative / debugger / qdeclarativeenginedebugservice.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
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.
17 **
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.
21 **
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.
29 **
30 ** Other Usage
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.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativeenginedebugservice_p.h"
43
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>
56
57 #include <QtCore/qdebug.h>
58 #include <QtCore/qmetaobject.h>
59
60 QT_BEGIN_NAMESPACE
61
62 Q_GLOBAL_STATIC(QDeclarativeEngineDebugService, qmlEngineDebugService);
63
64 QDeclarativeEngineDebugService *QDeclarativeEngineDebugService::instance()
65 {
66     return qmlEngineDebugService();
67 }
68
69 QDeclarativeEngineDebugService::QDeclarativeEngineDebugService(QObject *parent)
70     : QDeclarativeDebugService(QLatin1String("QDeclarativeEngine"), parent),
71       m_watch(new QDeclarativeWatcher(this))
72 {
73     QObject::connect(m_watch, SIGNAL(propertyChanged(int,int,QMetaProperty,QVariant)),
74                      this, SLOT(propertyChanged(int,int,QMetaProperty,QVariant)));
75 }
76
77 QDataStream &operator<<(QDataStream &ds, 
78                         const QDeclarativeEngineDebugService::QDeclarativeObjectData &data)
79 {
80     ds << data.url << data.lineNumber << data.columnNumber << data.idString
81        << data.objectName << data.objectType << data.objectId << data.contextId;
82     return ds;
83 }
84
85 QDataStream &operator>>(QDataStream &ds, 
86                         QDeclarativeEngineDebugService::QDeclarativeObjectData &data)
87 {
88     ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString
89        >> data.objectName >> data.objectType >> data.objectId >> data.contextId;
90     return ds;
91 }
92
93 QDataStream &operator<<(QDataStream &ds, 
94                         const QDeclarativeEngineDebugService::QDeclarativeObjectProperty &data)
95 {
96     ds << (int)data.type << data.name << data.value << data.valueTypeName
97        << data.binding << data.hasNotifySignal;
98     return ds;
99 }
100
101 QDataStream &operator>>(QDataStream &ds,  
102                         QDeclarativeEngineDebugService::QDeclarativeObjectProperty &data)
103 {
104     int type;
105     ds >> type >> data.name >> data.value >> data.valueTypeName
106        >> data.binding >> data.hasNotifySignal;
107     data.type = (QDeclarativeEngineDebugService::QDeclarativeObjectProperty::Type)type;
108     return ds;
109 }
110
111 static inline bool isSignalPropertyName(const QString &signalName)
112 {
113     // see QmlCompiler::isSignalPropertyName
114     return signalName.length() >= 3 && signalName.startsWith(QLatin1String("on")) &&
115             signalName.at(2).isLetter() && signalName.at(2).isUpper();
116 }
117
118 static bool hasValidSignal(QObject *object, const QString &propertyName)
119 {
120     if (!isSignalPropertyName(propertyName))
121         return false;
122
123     QString signalName = propertyName.mid(2);
124     signalName[0] = signalName.at(0).toLower();
125
126     int sigIdx = QDeclarativePropertyPrivate::findSignalByName(object->metaObject(), signalName.toLatin1()).methodIndex();
127
128     if (sigIdx == -1)
129         return false;
130
131     return true;
132 }
133
134 QDeclarativeEngineDebugService::QDeclarativeObjectProperty
135 QDeclarativeEngineDebugService::propertyData(QObject *obj, int propIdx)
136 {
137     QDeclarativeObjectProperty rv;
138
139     QMetaProperty prop = obj->metaObject()->property(propIdx);
140
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));
147     if (binding)
148         rv.binding = binding->expression();
149
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;
156     }
157
158     QVariant value;
159     if (rv.type != QDeclarativeObjectProperty::Unknown && prop.userType() != 0) {
160         value = prop.read(obj);
161     }
162     rv.value = valueContents(value);
163
164     return rv;
165 }
166
167 QVariant QDeclarativeEngineDebugService::valueContents(const QVariant &value) const
168 {
169     int userType = value.userType();
170
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));
177         return contents;
178     }
179
180     if (QDeclarativeValueTypeFactory::isValueType(userType))
181         return value;
182
183     if (QDeclarativeMetaType::isQObject(userType)) {
184         QObject *o = QDeclarativeMetaType::toQObject(value);
185         if (o) {
186             QString name = o->objectName();
187             if (name.isEmpty())
188                 name = QLatin1String("<unnamed object>");
189             return name;
190         }
191     }
192
193     return QLatin1String("<unknown value>");
194 }
195
196 void QDeclarativeEngineDebugService::buildObjectDump(QDataStream &message, 
197                                                      QObject *object, bool recur, bool dumpProperties)
198 {
199     message << objectData(object);
200
201     QObjectList children = object->children();
202     
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]))
206             --childrenCount;
207     }
208
209     message << childrenCount << recur;
210
211     QList<QDeclarativeObjectProperty> fakeProperties;
212
213     for (int ii = 0; ii < children.count(); ++ii) {
214         QObject *child = children.at(ii);
215         if (qobject_cast<QDeclarativeContext*>(child))
216             continue;
217         QDeclarativeBoundSignal *signal = QDeclarativeBoundSignal::cast(child);
218         if (signal) {
219             if (!dumpProperties)
220                 continue;
221             QDeclarativeObjectProperty prop;
222             prop.type = QDeclarativeObjectProperty::SignalProperty;
223             prop.hasNotifySignal = false;
224             QDeclarativeExpression *expr = signal->expression();
225             if (expr) {
226                 prop.value = expr->expression();
227                 QObject *scope = expr->scopeObject();
228                 if (scope) {
229                     QString sig = QLatin1String(scope->metaObject()->method(signal->index()).signature());
230                     int lparen = sig.indexOf(QLatin1Char('('));
231                     if (lparen >= 0) {
232                         QString methodName = sig.mid(0, lparen);
233                         prop.name = QLatin1String("on") + methodName[0].toUpper()
234                                 + methodName.mid(1);
235                     }
236                 }
237             }
238             fakeProperties << prop;
239         } else {
240             if (recur)
241                 buildObjectDump(message, child, recur, dumpProperties);
242             else
243                 message << objectData(child);
244         }
245     }
246
247     if (!dumpProperties) {
248         message << 0;
249         return;
250     }
251
252     QList<int> propertyIndexes;
253     for (int ii = 0; ii < object->metaObject()->propertyCount(); ++ii) {
254         if (object->metaObject()->property(ii).isScriptable())
255             propertyIndexes << ii;
256     }
257
258     message << propertyIndexes.size() + fakeProperties.count();
259
260     for (int ii = 0; ii < propertyIndexes.size(); ++ii)
261         message << propertyData(object, propertyIndexes.at(ii));
262
263     for (int ii = 0; ii < fakeProperties.count(); ++ii)
264         message << fakeProperties[ii];
265 }
266
267 void QDeclarativeEngineDebugService::prepareDeferredObjects(QObject *obj)
268 {
269     qmlExecuteDeferred(obj);
270
271     QObjectList children = obj->children();
272     for (int ii = 0; ii < children.count(); ++ii) {
273         QObject *child = children.at(ii);
274         prepareDeferredObjects(child);
275     }
276
277 }
278
279 void QDeclarativeEngineDebugService::buildObjectList(QDataStream &message, QDeclarativeContext *ctxt)
280 {
281     QDeclarativeContextData *p = QDeclarativeContextData::get(ctxt);
282
283     QString ctxtName = ctxt->objectName();
284     int ctxtId = QDeclarativeDebugService::idForObject(ctxt);
285
286     message << ctxtName << ctxtId;
287
288     int count = 0;
289
290     QDeclarativeContextData *child = p->childContexts;
291     while (child) {
292         ++count;
293         child = child->nextChild;
294     }
295
296     message << count;
297
298     child = p->childContexts;
299     while (child) {
300         buildObjectList(message, child->asQDeclarativeContext());
301         child = child->nextChild;
302     }
303
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);
309             --ii;
310         }
311     }
312
313     message << ctxtPriv->instances.count();
314     for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) {
315         message << objectData(ctxtPriv->instances.at(ii));
316     }
317 }
318
319 void QDeclarativeEngineDebugService::buildStatesList(QDeclarativeContext *ctxt, bool cleanList=false)
320 {
321     if (cleanList)
322         m_allStates.clear();
323
324     QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(ctxt);
325     for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) {
326         buildStatesList(ctxtPriv->instances.at(ii));
327     }
328
329     QDeclarativeContextData *child = QDeclarativeContextData::get(ctxt)->childContexts;
330     while (child) {
331         buildStatesList(child->asQDeclarativeContext());
332         child = child->nextChild;
333     }
334 }
335
336 void QDeclarativeEngineDebugService::buildStatesList(QObject *obj)
337 {
338     if (QDeclarativeState *state = qobject_cast<QDeclarativeState *>(obj)) {
339         m_allStates.append(state);
340     }
341
342     QObjectList children = obj->children();
343     for (int ii = 0; ii < children.count(); ++ii) {
344         buildStatesList(children.at(ii));
345     }
346 }
347
348 QDeclarativeEngineDebugService::QDeclarativeObjectData
349 QDeclarativeEngineDebugService::objectData(QObject *object)
350 {
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;
357     } else {
358         rv.lineNumber = -1;
359         rv.columnNumber = -1;
360     }
361
362     QDeclarativeContext *context = qmlContext(object);
363     if (context) {
364         QDeclarativeContextData *cdata = QDeclarativeContextData::get(context);
365         if (cdata)
366             rv.idString = cdata->findObjectId(object);
367     }
368
369     rv.objectName = object->objectName();
370     rv.objectId = QDeclarativeDebugService::idForObject(object);
371     rv.contextId = QDeclarativeDebugService::idForObject(qmlContext(object));
372
373     QDeclarativeType *type = QDeclarativeMetaType::qmlType(object->metaObject());
374     if (type) {
375         QString typeName = type->qmlTypeName();
376         int lastSlash = typeName.lastIndexOf(QLatin1Char('/'));
377         rv.objectType = lastSlash < 0 ? typeName : typeName.mid(lastSlash+1);
378     } else {
379         rv.objectType = QString::fromUtf8(object->metaObject()->className());
380         int marker = rv.objectType.indexOf(QLatin1String("_QMLTYPE_"));
381         if (marker != -1)
382             rv.objectType = rv.objectType.left(marker);
383     }
384
385     return rv;
386 }
387
388 void QDeclarativeEngineDebugService::messageReceived(const QByteArray &message)
389 {
390     QMetaObject::invokeMethod(this, "processMessage", Qt::QueuedConnection, Q_ARG(QByteArray, message));
391 }
392
393 void QDeclarativeEngineDebugService::processMessage(const QByteArray &message)
394 {
395     QDataStream ds(message);
396
397     QByteArray type;
398     ds >> type;
399
400     if (type == "LIST_ENGINES") {
401         int queryId;
402         ds >> queryId;
403
404         QByteArray reply;
405         QDataStream rs(&reply, QIODevice::WriteOnly);
406         rs << QByteArray("LIST_ENGINES_R");
407         rs << queryId << m_engines.count();
408
409         for (int ii = 0; ii < m_engines.count(); ++ii) {
410             QDeclarativeEngine *engine = m_engines.at(ii);
411
412             QString engineName = engine->objectName();
413             int engineId = QDeclarativeDebugService::idForObject(engine);
414
415             rs << engineName << engineId;
416         }
417
418         sendMessage(reply);
419     } else if (type == "LIST_OBJECTS") {
420         int queryId;
421         int engineId = -1;
422         ds >> queryId >> engineId;
423
424         QDeclarativeEngine *engine =
425                 qobject_cast<QDeclarativeEngine *>(QDeclarativeDebugService::objectForId(engineId));
426
427         QByteArray reply;
428         QDataStream rs(&reply, QIODevice::WriteOnly);
429         rs << QByteArray("LIST_OBJECTS_R") << queryId;
430
431         if (engine) {
432             buildObjectList(rs, engine->rootContext());
433             buildStatesList(engine->rootContext(), true);
434         }
435
436         sendMessage(reply);
437     } else if (type == "FETCH_OBJECT") {
438         int queryId;
439         int objectId;
440         bool recurse;
441         bool dumpProperties = true;
442
443         ds >> queryId >> objectId >> recurse >> dumpProperties;
444
445         QObject *object = QDeclarativeDebugService::objectForId(objectId);
446
447         QByteArray reply;
448         QDataStream rs(&reply, QIODevice::WriteOnly);
449         rs << QByteArray("FETCH_OBJECT_R") << queryId;
450
451         if (object) {
452             if (recurse)
453                 prepareDeferredObjects(object);
454             buildObjectDump(rs, object, recurse, dumpProperties);
455         }
456
457         sendMessage(reply);
458     } else if (type == "WATCH_OBJECT") {
459         int queryId;
460         int objectId;
461
462         ds >> queryId >> objectId;
463         bool ok = m_watch->addWatch(queryId, objectId);
464
465         QByteArray reply;
466         QDataStream rs(&reply, QIODevice::WriteOnly);
467         rs << QByteArray("WATCH_OBJECT_R") << queryId << ok;
468
469         sendMessage(reply);
470     } else if (type == "WATCH_PROPERTY") {
471         int queryId;
472         int objectId;
473         QByteArray property;
474
475         ds >> queryId >> objectId >> property;
476         bool ok = m_watch->addWatch(queryId, objectId, property);
477
478         QByteArray reply;
479         QDataStream rs(&reply, QIODevice::WriteOnly);
480         rs << QByteArray("WATCH_PROPERTY_R") << queryId << ok;
481
482         sendMessage(reply);
483     } else if (type == "WATCH_EXPR_OBJECT") {
484         int queryId;
485         int debugId;
486         QString expr;
487
488         ds >> queryId >> debugId >> expr;
489         bool ok = m_watch->addWatch(queryId, debugId, expr);
490
491         QByteArray reply;
492         QDataStream rs(&reply, QIODevice::WriteOnly);
493         rs << QByteArray("WATCH_EXPR_OBJECT_R") << queryId << ok;
494         sendMessage(reply);
495     } else if (type == "NO_WATCH") {
496         int queryId;
497
498         ds >> queryId;
499         m_watch->removeWatch(queryId);
500     } else if (type == "EVAL_EXPRESSION") {
501         int queryId;
502         int objectId;
503         QString expr;
504
505         ds >> queryId >> objectId >> expr;
506
507         QObject *object = QDeclarativeDebugService::objectForId(objectId);
508         QDeclarativeContext *context = qmlContext(object);
509         QVariant result;
510         if (object && context) {
511             QDeclarativeExpression exprObj(context, object, expr);
512             bool undefined = false;
513             QVariant value = exprObj.evaluate(&undefined);
514             if (undefined)
515                 result = QLatin1String("<undefined>");
516             else
517                 result = valueContents(value);
518         } else {
519             result = QLatin1String("<unknown context>");
520         }
521
522         QByteArray reply;
523         QDataStream rs(&reply, QIODevice::WriteOnly);
524         rs << QByteArray("EVAL_EXPRESSION_R") << queryId << result;
525
526         sendMessage(reply);
527     } else if (type == "SET_BINDING") {
528         int objectId;
529         QString propertyName;
530         QVariant expr;
531         bool isLiteralValue;
532         QString filename;
533         int line;
534         ds >> objectId >> propertyName >> expr >> isLiteralValue;
535         if (!ds.atEnd()) { // backward compatibility from 2.1, 2.2
536             ds >> filename >> line;
537         }
538         setBinding(objectId, propertyName, expr, isLiteralValue, filename, line);
539     } else if (type == "RESET_BINDING") {
540         int objectId;
541         QString propertyName;
542         ds >> objectId >> propertyName;
543         resetBinding(objectId, propertyName);
544     } else if (type == "SET_METHOD_BODY") {
545         int objectId;
546         QString methodName;
547         QString methodBody;
548         ds >> objectId >> methodName >> methodBody;
549         setMethodBody(objectId, methodName, methodBody);
550     }
551 }
552
553 void QDeclarativeEngineDebugService::setBinding(int objectId,
554                                                 const QString &propertyName,
555                                                 const QVariant &expression,
556                                                 bool isLiteralValue,
557                                                 QString filename,
558                                                 int line)
559 {
560     QObject *object = objectForId(objectId);
561     QDeclarativeContext *context = qmlContext(object);
562
563     if (object && context) {
564         QDeclarativeProperty property(object, propertyName, context);
565         if (property.isValid()) {
566
567             bool inBaseState = true;
568
569             foreach(QWeakPointer<QDeclarativeState> statePointer, m_allStates) {
570                 if (QDeclarativeState *state = statePointer.data()) {
571                     // here we assume that the revert list on itself defines the base state
572                     if (state->isStateActive() && state->containsPropertyInRevertList(object, propertyName)) {
573                         inBaseState = false;
574
575                         QDeclarativeBinding *newBinding = 0;
576                         if (!isLiteralValue) {
577                             newBinding = new QDeclarativeBinding(expression.toString(), object, context);
578                             newBinding->setTarget(property);
579                             newBinding->setNotifyOnValueChanged(true);
580                             newBinding->setSourceLocation(filename, line);
581                         }
582
583                         state->changeBindingInRevertList(object, propertyName, newBinding);
584
585                         if (isLiteralValue)
586                             state->changeValueInRevertList(object, propertyName, expression);
587                     }
588                 }
589             }
590
591             if (inBaseState) {
592                 if (isLiteralValue) {
593                     property.write(expression);
594                 } else if (hasValidSignal(object, propertyName)) {
595                     QDeclarativeExpression *declarativeExpression = new QDeclarativeExpression(context, object, expression.toString());
596                     QDeclarativePropertyPrivate::setSignalExpression(property, declarativeExpression);
597                     declarativeExpression->setSourceLocation(filename, line);
598                 } else if (property.isProperty()) {
599                     QDeclarativeBinding *binding = new QDeclarativeBinding(expression.toString(), object, context);
600                     binding->setTarget(property);
601                     binding->setSourceLocation(filename, line);
602                     binding->setNotifyOnValueChanged(true);
603                     QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::setBinding(property, binding);
604                     if (oldBinding)
605                         oldBinding->destroy();
606                     binding->update();
607                 } else {
608                     qWarning() << "QDeclarativeEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object;
609                 }
610             }
611
612         } else {
613             // not a valid property
614             if (QDeclarativePropertyChanges *propertyChanges = qobject_cast<QDeclarativePropertyChanges *>(object)) {
615                 if (isLiteralValue) {
616                     propertyChanges->changeValue(propertyName, expression);
617                 } else {
618                     propertyChanges->changeExpression(propertyName, expression.toString());
619                 }
620             } else {
621                 qWarning() << "QDeclarativeEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object;
622             }
623         }
624     }
625 }
626
627 void QDeclarativeEngineDebugService::resetBinding(int objectId, const QString &propertyName)
628 {
629     QObject *object = objectForId(objectId);
630     QDeclarativeContext *context = qmlContext(object);
631
632     if (object && context) {
633         if (object->property(propertyName.toLatin1()).isValid()) {
634             QDeclarativeProperty property(object, propertyName);
635             QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(property);
636             if (oldBinding) {
637                 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::setBinding(property, 0);
638                 if (oldBinding)
639                     oldBinding->destroy();
640             }
641             if (property.isResettable()) {
642                 // Note: this will reset the property in any case, without regard to states
643                 // Right now almost no QDeclarativeItem has reset methods for its properties (with the
644                 // notable exception of QDeclarativeAnchors), so this is not a big issue
645                 // later on, setBinding does take states into account
646                 property.reset();
647             } else {
648                 // overwrite with default value
649                 if (QDeclarativeType *objType = QDeclarativeMetaType::qmlType(object->metaObject())) {
650                     if (QObject *emptyObject = objType->create()) {
651                         if (emptyObject->property(propertyName.toLatin1()).isValid()) {
652                             QVariant defaultValue = QDeclarativeProperty(emptyObject, propertyName).read();
653                             if (defaultValue.isValid()) {
654                                 setBinding(objectId, propertyName, defaultValue, true);
655                             }
656                         }
657                         delete emptyObject;
658                     }
659                 }
660             }
661         } else if (hasValidSignal(object, propertyName)) {
662             QDeclarativeProperty property(object, propertyName, context);
663             QDeclarativePropertyPrivate::setSignalExpression(property, 0);
664         } else {
665             if (QDeclarativePropertyChanges *propertyChanges = qobject_cast<QDeclarativePropertyChanges *>(object)) {
666                 propertyChanges->removeProperty(propertyName);
667             }
668         }
669     }
670 }
671
672 void QDeclarativeEngineDebugService::setMethodBody(int objectId, const QString &method, const QString &body)
673 {
674     QObject *object = objectForId(objectId);
675     QDeclarativeContext *context = qmlContext(object);
676     if (!object || !context || !context->engine())
677         return;
678     QDeclarativeContextData *contextData = QDeclarativeContextData::get(context);
679     if (!contextData)
680         return;
681
682     QDeclarativePropertyData dummy;
683     QDeclarativePropertyData *prop =
684             QDeclarativePropertyCache::property(context->engine(), object, method, dummy);
685
686     if (!prop || !prop->isVMEFunction())
687         return;
688
689     QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex);
690     QList<QByteArray> paramNames = metaMethod.parameterNames();
691
692     QString paramStr;
693     for (int ii = 0; ii < paramNames.count(); ++ii) {
694         if (ii != 0) paramStr.append(QLatin1String(","));
695         paramStr.append(QString::fromUtf8(paramNames.at(ii)));
696     }
697
698     QString jsfunction = QLatin1String("(function ") + method + QLatin1String("(") + paramStr +
699             QLatin1String(") {");
700     jsfunction += body;
701     jsfunction += QLatin1String("\n})");
702
703     QDeclarativeVMEMetaObject *vmeMetaObject =
704             static_cast<QDeclarativeVMEMetaObject*>(QObjectPrivate::get(object)->metaObject);
705     Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this
706
707     int lineNumber = vmeMetaObject->vmeMethodLineNumber(prop->coreIndex);
708     vmeMetaObject->setVmeMethod(prop->coreIndex, QDeclarativeExpressionPrivate::evalFunction(contextData, object, jsfunction, contextData->url.toString(), lineNumber));
709 }
710
711 void QDeclarativeEngineDebugService::propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value)
712 {
713     QByteArray reply;
714     QDataStream rs(&reply, QIODevice::WriteOnly);
715
716     rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value);
717
718     sendMessage(reply);
719 }
720
721 void QDeclarativeEngineDebugService::addEngine(QDeclarativeEngine *engine)
722 {
723     Q_ASSERT(engine);
724     Q_ASSERT(!m_engines.contains(engine));
725
726     m_engines.append(engine);
727 }
728
729 void QDeclarativeEngineDebugService::remEngine(QDeclarativeEngine *engine)
730 {
731     Q_ASSERT(engine);
732     Q_ASSERT(m_engines.contains(engine));
733
734     m_engines.removeAll(engine);
735 }
736
737 void QDeclarativeEngineDebugService::objectCreated(QDeclarativeEngine *engine, QObject *object)
738 {
739     Q_ASSERT(engine);
740     Q_ASSERT(m_engines.contains(engine));
741
742     int engineId = QDeclarativeDebugService::idForObject(engine);
743     int objectId = QDeclarativeDebugService::idForObject(object);
744
745     QByteArray reply;
746     QDataStream rs(&reply, QIODevice::WriteOnly);
747
748     rs << QByteArray("OBJECT_CREATED") << engineId << objectId;
749     sendMessage(reply);
750 }
751
752 QT_END_NAMESPACE