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