|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 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 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "private/qdeclarativeenginedebug_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 |
|
54 #include <QtCore/qdebug.h> |
|
55 #include <QtCore/qmetaobject.h> |
|
56 |
|
57 QT_BEGIN_NAMESPACE |
|
58 |
|
59 QList<QDeclarativeEngine *> QDeclarativeEngineDebugServer::m_engines; |
|
60 QDeclarativeEngineDebugServer::QDeclarativeEngineDebugServer(QObject *parent) |
|
61 : QDeclarativeDebugService(QLatin1String("QDeclarativeEngine"), parent), |
|
62 m_watch(new QDeclarativeWatcher(this)) |
|
63 { |
|
64 QObject::connect(m_watch, SIGNAL(propertyChanged(int,int,QMetaProperty,QVariant)), |
|
65 this, SLOT(propertyChanged(int,int,QMetaProperty,QVariant))); |
|
66 } |
|
67 |
|
68 QDataStream &operator<<(QDataStream &ds, |
|
69 const QDeclarativeEngineDebugServer::QDeclarativeObjectData &data) |
|
70 { |
|
71 ds << data.url << data.lineNumber << data.columnNumber << data.idString |
|
72 << data.objectName << data.objectType << data.objectId << data.contextId; |
|
73 return ds; |
|
74 } |
|
75 |
|
76 QDataStream &operator>>(QDataStream &ds, |
|
77 QDeclarativeEngineDebugServer::QDeclarativeObjectData &data) |
|
78 { |
|
79 ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString |
|
80 >> data.objectName >> data.objectType >> data.objectId >> data.contextId; |
|
81 return ds; |
|
82 } |
|
83 |
|
84 QDataStream &operator<<(QDataStream &ds, |
|
85 const QDeclarativeEngineDebugServer::QDeclarativeObjectProperty &data) |
|
86 { |
|
87 ds << (int)data.type << data.name << data.value << data.valueTypeName |
|
88 << data.binding << data.hasNotifySignal; |
|
89 return ds; |
|
90 } |
|
91 |
|
92 QDataStream &operator>>(QDataStream &ds, |
|
93 QDeclarativeEngineDebugServer::QDeclarativeObjectProperty &data) |
|
94 { |
|
95 int type; |
|
96 ds >> type >> data.name >> data.value >> data.valueTypeName |
|
97 >> data.binding >> data.hasNotifySignal; |
|
98 data.type = (QDeclarativeEngineDebugServer::QDeclarativeObjectProperty::Type)type; |
|
99 return ds; |
|
100 } |
|
101 |
|
102 QDeclarativeEngineDebugServer::QDeclarativeObjectProperty |
|
103 QDeclarativeEngineDebugServer::propertyData(QObject *obj, int propIdx) |
|
104 { |
|
105 QDeclarativeObjectProperty rv; |
|
106 |
|
107 QMetaProperty prop = obj->metaObject()->property(propIdx); |
|
108 |
|
109 rv.type = QDeclarativeObjectProperty::Unknown; |
|
110 rv.valueTypeName = QString::fromUtf8(prop.typeName()); |
|
111 rv.name = QString::fromUtf8(prop.name()); |
|
112 rv.hasNotifySignal = prop.hasNotifySignal(); |
|
113 QDeclarativeAbstractBinding *binding = |
|
114 QDeclarativePropertyPrivate::binding(QDeclarativeProperty(obj, rv.name)); |
|
115 if (binding) |
|
116 rv.binding = binding->expression(); |
|
117 |
|
118 QVariant value = prop.read(obj); |
|
119 rv.value = valueContents(value); |
|
120 |
|
121 if (QDeclarativeValueTypeFactory::isValueType(prop.userType())) { |
|
122 rv.type = QDeclarativeObjectProperty::Basic; |
|
123 } else if (QDeclarativeMetaType::isQObject(prop.userType())) { |
|
124 rv.type = QDeclarativeObjectProperty::Object; |
|
125 } else if (QDeclarativeMetaType::isList(prop.userType())) { |
|
126 rv.type = QDeclarativeObjectProperty::List; |
|
127 } |
|
128 |
|
129 return rv; |
|
130 } |
|
131 |
|
132 QVariant QDeclarativeEngineDebugServer::valueContents(const QVariant &value) const |
|
133 { |
|
134 int userType = value.userType(); |
|
135 if (QDeclarativeValueTypeFactory::isValueType(userType)) |
|
136 return value; |
|
137 |
|
138 /* |
|
139 if (QDeclarativeMetaType::isList(userType)) { |
|
140 int count = QDeclarativeMetaType::listCount(value); |
|
141 QVariantList contents; |
|
142 for (int i=0; i<count; i++) |
|
143 contents << valueContents(QDeclarativeMetaType::listAt(value, i)); |
|
144 return contents; |
|
145 } else */ |
|
146 if (QDeclarativeMetaType::isQObject(userType)) { |
|
147 QObject *o = QDeclarativeMetaType::toQObject(value); |
|
148 if (o) { |
|
149 QString name = o->objectName(); |
|
150 if (name.isEmpty()) |
|
151 name = QLatin1String("<unnamed object>"); |
|
152 return name; |
|
153 } |
|
154 } |
|
155 |
|
156 return QLatin1String("<unknown value>"); |
|
157 } |
|
158 |
|
159 void QDeclarativeEngineDebugServer::buildObjectDump(QDataStream &message, |
|
160 QObject *object, bool recur) |
|
161 { |
|
162 message << objectData(object); |
|
163 |
|
164 // Some children aren't added to an object until particular properties are read |
|
165 // - e.g. child state objects aren't added until the 'states' property is read - |
|
166 // but this should only affect internal objects that aren't shown by the |
|
167 // debugger anyway. |
|
168 |
|
169 QObjectList children = object->children(); |
|
170 |
|
171 int childrenCount = children.count(); |
|
172 for (int ii = 0; ii < children.count(); ++ii) { |
|
173 if (qobject_cast<QDeclarativeContext*>(children[ii]) || QDeclarativeBoundSignal::cast(children[ii])) |
|
174 --childrenCount; |
|
175 } |
|
176 |
|
177 message << childrenCount << recur; |
|
178 |
|
179 QList<QDeclarativeObjectProperty> fakeProperties; |
|
180 |
|
181 for (int ii = 0; ii < children.count(); ++ii) { |
|
182 QObject *child = children.at(ii); |
|
183 if (qobject_cast<QDeclarativeContext*>(child)) |
|
184 continue; |
|
185 QDeclarativeBoundSignal *signal = QDeclarativeBoundSignal::cast(child); |
|
186 if (signal) { |
|
187 QDeclarativeObjectProperty prop; |
|
188 prop.type = QDeclarativeObjectProperty::SignalProperty; |
|
189 prop.hasNotifySignal = false; |
|
190 QDeclarativeExpression *expr = signal->expression(); |
|
191 if (expr) { |
|
192 prop.value = expr->expression(); |
|
193 QObject *scope = expr->scopeObject(); |
|
194 if (scope) { |
|
195 QString sig = QLatin1String(scope->metaObject()->method(signal->index()).signature()); |
|
196 int lparen = sig.indexOf(QLatin1Char('(')); |
|
197 if (lparen >= 0) { |
|
198 QString methodName = sig.mid(0, lparen); |
|
199 prop.name = QLatin1String("on") + methodName[0].toUpper() |
|
200 + methodName.mid(1); |
|
201 } |
|
202 } |
|
203 } |
|
204 fakeProperties << prop; |
|
205 } else { |
|
206 if (recur) |
|
207 buildObjectDump(message, child, recur); |
|
208 else |
|
209 message << objectData(child); |
|
210 } |
|
211 } |
|
212 |
|
213 message << (object->metaObject()->propertyCount() + fakeProperties.count()); |
|
214 |
|
215 for (int ii = 0; ii < object->metaObject()->propertyCount(); ++ii) |
|
216 message << propertyData(object, ii); |
|
217 |
|
218 for (int ii = 0; ii < fakeProperties.count(); ++ii) |
|
219 message << fakeProperties[ii]; |
|
220 } |
|
221 |
|
222 void QDeclarativeEngineDebugServer::buildObjectList(QDataStream &message, QDeclarativeContext *ctxt) |
|
223 { |
|
224 QDeclarativeContextData *p = QDeclarativeContextData::get(ctxt); |
|
225 |
|
226 QString ctxtName = ctxt->objectName(); |
|
227 int ctxtId = QDeclarativeDebugService::idForObject(ctxt); |
|
228 |
|
229 message << ctxtName << ctxtId; |
|
230 |
|
231 int count = 0; |
|
232 |
|
233 QDeclarativeContextData *child = p->childContexts; |
|
234 while (child) { |
|
235 if (!child->isInternal) |
|
236 ++count; |
|
237 child = child->nextChild; |
|
238 } |
|
239 |
|
240 message << count; |
|
241 |
|
242 child = p->childContexts; |
|
243 while (child) { |
|
244 if (!child->isInternal) |
|
245 buildObjectList(message, child->asQDeclarativeContext()); |
|
246 child = child->nextChild; |
|
247 } |
|
248 |
|
249 // Clean deleted objects |
|
250 QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(ctxt); |
|
251 for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) { |
|
252 if (!ctxtPriv->instances.at(ii)) { |
|
253 ctxtPriv->instances.removeAt(ii); |
|
254 --ii; |
|
255 } |
|
256 } |
|
257 |
|
258 message << ctxtPriv->instances.count(); |
|
259 for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) { |
|
260 message << objectData(ctxtPriv->instances.at(ii)); |
|
261 } |
|
262 } |
|
263 |
|
264 QDeclarativeEngineDebugServer::QDeclarativeObjectData |
|
265 QDeclarativeEngineDebugServer::objectData(QObject *object) |
|
266 { |
|
267 QDeclarativeData *ddata = QDeclarativeData::get(object); |
|
268 QDeclarativeObjectData rv; |
|
269 if (ddata && ddata->outerContext) { |
|
270 rv.url = ddata->outerContext->url; |
|
271 rv.lineNumber = ddata->lineNumber; |
|
272 rv.columnNumber = ddata->columnNumber; |
|
273 } else { |
|
274 rv.lineNumber = -1; |
|
275 rv.columnNumber = -1; |
|
276 } |
|
277 |
|
278 QDeclarativeContext *context = qmlContext(object); |
|
279 if (context) { |
|
280 QDeclarativeContextData *cdata = QDeclarativeContextData::get(context); |
|
281 if (cdata) |
|
282 rv.idString = cdata->findObjectId(object); |
|
283 } |
|
284 |
|
285 rv.objectName = object->objectName(); |
|
286 rv.objectId = QDeclarativeDebugService::idForObject(object); |
|
287 rv.contextId = QDeclarativeDebugService::idForObject(qmlContext(object)); |
|
288 |
|
289 QDeclarativeType *type = QDeclarativeMetaType::qmlType(object->metaObject()); |
|
290 if (type) { |
|
291 QString typeName = QLatin1String(type->qmlTypeName()); |
|
292 int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); |
|
293 rv.objectType = lastSlash < 0 ? typeName : typeName.mid(lastSlash+1); |
|
294 } else { |
|
295 rv.objectType = QString::fromUtf8(object->metaObject()->className()); |
|
296 int marker = rv.objectType.indexOf(QLatin1String("_QMLTYPE_")); |
|
297 if (marker != -1) |
|
298 rv.objectType = rv.objectType.left(marker); |
|
299 } |
|
300 |
|
301 return rv; |
|
302 } |
|
303 |
|
304 void QDeclarativeEngineDebugServer::messageReceived(const QByteArray &message) |
|
305 { |
|
306 QDataStream ds(message); |
|
307 |
|
308 QByteArray type; |
|
309 ds >> type; |
|
310 |
|
311 if (type == "LIST_ENGINES") { |
|
312 int queryId; |
|
313 ds >> queryId; |
|
314 |
|
315 QByteArray reply; |
|
316 QDataStream rs(&reply, QIODevice::WriteOnly); |
|
317 rs << QByteArray("LIST_ENGINES_R"); |
|
318 rs << queryId << m_engines.count(); |
|
319 |
|
320 for (int ii = 0; ii < m_engines.count(); ++ii) { |
|
321 QDeclarativeEngine *engine = m_engines.at(ii); |
|
322 |
|
323 QString engineName = engine->objectName(); |
|
324 int engineId = QDeclarativeDebugService::idForObject(engine); |
|
325 |
|
326 rs << engineName << engineId; |
|
327 } |
|
328 |
|
329 sendMessage(reply); |
|
330 } else if (type == "LIST_OBJECTS") { |
|
331 int queryId; |
|
332 int engineId = -1; |
|
333 ds >> queryId >> engineId; |
|
334 |
|
335 QDeclarativeEngine *engine = |
|
336 qobject_cast<QDeclarativeEngine *>(QDeclarativeDebugService::objectForId(engineId)); |
|
337 |
|
338 QByteArray reply; |
|
339 QDataStream rs(&reply, QIODevice::WriteOnly); |
|
340 rs << QByteArray("LIST_OBJECTS_R") << queryId; |
|
341 |
|
342 if (engine) |
|
343 buildObjectList(rs, engine->rootContext()); |
|
344 |
|
345 sendMessage(reply); |
|
346 } else if (type == "FETCH_OBJECT") { |
|
347 int queryId; |
|
348 int objectId; |
|
349 bool recurse; |
|
350 |
|
351 ds >> queryId >> objectId >> recurse; |
|
352 |
|
353 QObject *object = QDeclarativeDebugService::objectForId(objectId); |
|
354 |
|
355 QByteArray reply; |
|
356 QDataStream rs(&reply, QIODevice::WriteOnly); |
|
357 rs << QByteArray("FETCH_OBJECT_R") << queryId; |
|
358 |
|
359 if (object) |
|
360 buildObjectDump(rs, object, recurse); |
|
361 |
|
362 sendMessage(reply); |
|
363 } else if (type == "WATCH_OBJECT") { |
|
364 int queryId; |
|
365 int objectId; |
|
366 |
|
367 ds >> queryId >> objectId; |
|
368 bool ok = m_watch->addWatch(queryId, objectId); |
|
369 |
|
370 QByteArray reply; |
|
371 QDataStream rs(&reply, QIODevice::WriteOnly); |
|
372 rs << QByteArray("WATCH_OBJECT_R") << queryId << ok; |
|
373 |
|
374 sendMessage(reply); |
|
375 } else if (type == "WATCH_PROPERTY") { |
|
376 int queryId; |
|
377 int objectId; |
|
378 QByteArray property; |
|
379 |
|
380 ds >> queryId >> objectId >> property; |
|
381 bool ok = m_watch->addWatch(queryId, objectId, property); |
|
382 |
|
383 QByteArray reply; |
|
384 QDataStream rs(&reply, QIODevice::WriteOnly); |
|
385 rs << QByteArray("WATCH_PROPERTY_R") << queryId << ok; |
|
386 |
|
387 sendMessage(reply); |
|
388 } else if (type == "WATCH_EXPR_OBJECT") { |
|
389 int queryId; |
|
390 int debugId; |
|
391 QString expr; |
|
392 |
|
393 ds >> queryId >> debugId >> expr; |
|
394 bool ok = m_watch->addWatch(queryId, debugId, expr); |
|
395 |
|
396 QByteArray reply; |
|
397 QDataStream rs(&reply, QIODevice::WriteOnly); |
|
398 rs << QByteArray("WATCH_EXPR_OBJECT_R") << queryId << ok; |
|
399 |
|
400 sendMessage(reply); |
|
401 } else if (type == "NO_WATCH") { |
|
402 int queryId; |
|
403 |
|
404 ds >> queryId; |
|
405 m_watch->removeWatch(queryId); |
|
406 } else if (type == "EVAL_EXPRESSION") { |
|
407 int queryId; |
|
408 int objectId; |
|
409 QString expr; |
|
410 |
|
411 ds >> queryId >> objectId >> expr; |
|
412 |
|
413 QObject *object = QDeclarativeDebugService::objectForId(objectId); |
|
414 QDeclarativeContext *context = qmlContext(object); |
|
415 QVariant result; |
|
416 if (object && context) { |
|
417 QDeclarativeExpression exprObj(context, object, expr); |
|
418 bool undefined = false; |
|
419 QVariant value = exprObj.evaluate(&undefined); |
|
420 if (undefined) |
|
421 result = QLatin1String("<undefined>"); |
|
422 else |
|
423 result = valueContents(value); |
|
424 } else { |
|
425 result = QLatin1String("<unknown context>"); |
|
426 } |
|
427 |
|
428 QByteArray reply; |
|
429 QDataStream rs(&reply, QIODevice::WriteOnly); |
|
430 rs << QByteArray("EVAL_EXPRESSION_R") << queryId << result; |
|
431 |
|
432 sendMessage(reply); |
|
433 } |
|
434 } |
|
435 |
|
436 void QDeclarativeEngineDebugServer::propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value) |
|
437 { |
|
438 QByteArray reply; |
|
439 QDataStream rs(&reply, QIODevice::WriteOnly); |
|
440 |
|
441 rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value); |
|
442 |
|
443 sendMessage(reply); |
|
444 } |
|
445 |
|
446 void QDeclarativeEngineDebugServer::addEngine(QDeclarativeEngine *engine) |
|
447 { |
|
448 Q_ASSERT(engine); |
|
449 Q_ASSERT(!m_engines.contains(engine)); |
|
450 |
|
451 m_engines.append(engine); |
|
452 } |
|
453 |
|
454 void QDeclarativeEngineDebugServer::remEngine(QDeclarativeEngine *engine) |
|
455 { |
|
456 Q_ASSERT(engine); |
|
457 Q_ASSERT(m_engines.contains(engine)); |
|
458 |
|
459 m_engines.removeAll(engine); |
|
460 } |
|
461 |
|
462 QT_END_NAMESPACE |