source: trunk/src/declarative/qml/qdeclarativeenginedebug.cpp@ 900

Last change on this file since 900 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 21.7 KB
Line 
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** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
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#include "private/qdeclarativevmemetaobject_p.h"
54#include "private/qdeclarativeexpression_p.h"
55
56#include <QtCore/qdebug.h>
57#include <QtCore/qmetaobject.h>
58
59QT_BEGIN_NAMESPACE
60
61Q_GLOBAL_STATIC(QDeclarativeEngineDebugServer, qmlEngineDebugServer);
62
63QDeclarativeEngineDebugServer *QDeclarativeEngineDebugServer::instance()
64{
65 return qmlEngineDebugServer();
66}
67
68QDeclarativeEngineDebugServer::QDeclarativeEngineDebugServer(QObject *parent)
69: QDeclarativeDebugService(QLatin1String("QDeclarativeEngine"), parent),
70 m_watch(new QDeclarativeWatcher(this))
71{
72 QObject::connect(m_watch, SIGNAL(propertyChanged(int,int,QMetaProperty,QVariant)),
73 this, SLOT(propertyChanged(int,int,QMetaProperty,QVariant)));
74}
75
76QDataStream &operator<<(QDataStream &ds,
77 const 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
84QDataStream &operator>>(QDataStream &ds,
85 QDeclarativeEngineDebugServer::QDeclarativeObjectData &data)
86{
87 ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString
88 >> data.objectName >> data.objectType >> data.objectId >> data.contextId;
89 return ds;
90}
91
92QDataStream &operator<<(QDataStream &ds,
93 const QDeclarativeEngineDebugServer::QDeclarativeObjectProperty &data)
94{
95 ds << (int)data.type << data.name << data.value << data.valueTypeName
96 << data.binding << data.hasNotifySignal;
97 return ds;
98}
99
100QDataStream &operator>>(QDataStream &ds,
101 QDeclarativeEngineDebugServer::QDeclarativeObjectProperty &data)
102{
103 int type;
104 ds >> type >> data.name >> data.value >> data.valueTypeName
105 >> data.binding >> data.hasNotifySignal;
106 data.type = (QDeclarativeEngineDebugServer::QDeclarativeObjectProperty::Type)type;
107 return ds;
108}
109
110static inline bool isSignalPropertyName(const QString &signalName)
111{
112 // see QmlCompiler::isSignalPropertyName
113 return signalName.length() >= 3 && signalName.startsWith(QLatin1String("on")) &&
114 signalName.at(2).isLetter() && signalName.at(2).isUpper();
115}
116
117static bool hasValidSignal(QObject *object, const QString &propertyName)
118{
119 if (!isSignalPropertyName(propertyName))
120 return false;
121
122 QString signalName = propertyName.mid(2);
123 signalName[0] = signalName.at(0).toLower();
124
125 int sigIdx = QDeclarativePropertyPrivate::findSignalByName(object->metaObject(), signalName.toLatin1()).methodIndex();
126
127 if (sigIdx == -1)
128 return false;
129
130 return true;
131}
132
133QDeclarativeEngineDebugServer::QDeclarativeObjectProperty
134QDeclarativeEngineDebugServer::propertyData(QObject *obj, int propIdx)
135{
136 QDeclarativeObjectProperty rv;
137
138 QMetaProperty prop = obj->metaObject()->property(propIdx);
139
140 rv.type = QDeclarativeObjectProperty::Unknown;
141 rv.valueTypeName = QString::fromUtf8(prop.typeName());
142 rv.name = QString::fromUtf8(prop.name());
143 rv.hasNotifySignal = prop.hasNotifySignal();
144 QDeclarativeAbstractBinding *binding =
145 QDeclarativePropertyPrivate::binding(QDeclarativeProperty(obj, rv.name));
146 if (binding)
147 rv.binding = binding->expression();
148
149 QVariant value;
150 if (prop.userType() != 0) {
151 value = prop.read(obj);
152 }
153 rv.value = valueContents(value);
154
155 if (QDeclarativeValueTypeFactory::isValueType(prop.userType())) {
156 rv.type = QDeclarativeObjectProperty::Basic;
157 } else if (QDeclarativeMetaType::isQObject(prop.userType())) {
158 rv.type = QDeclarativeObjectProperty::Object;
159 } else if (QDeclarativeMetaType::isList(prop.userType())) {
160 rv.type = QDeclarativeObjectProperty::List;
161 }
162
163 return rv;
164}
165
166QVariant QDeclarativeEngineDebugServer::valueContents(const QVariant &value) const
167{
168 int userType = value.userType();
169 if (QDeclarativeValueTypeFactory::isValueType(userType))
170 return value;
171
172 /*
173 if (QDeclarativeMetaType::isList(userType)) {
174 int count = QDeclarativeMetaType::listCount(value);
175 QVariantList contents;
176 for (int i=0; i<count; i++)
177 contents << valueContents(QDeclarativeMetaType::listAt(value, i));
178 return contents;
179 } else */
180 if (QDeclarativeMetaType::isQObject(userType)) {
181 QObject *o = QDeclarativeMetaType::toQObject(value);
182 if (o) {
183 QString name = o->objectName();
184 if (name.isEmpty())
185 name = QLatin1String("<unnamed object>");
186 return name;
187 }
188 }
189
190 return QLatin1String("<unknown value>");
191}
192
193void QDeclarativeEngineDebugServer::buildObjectDump(QDataStream &message,
194 QObject *object, bool recur, bool dumpProperties)
195{
196 message << objectData(object);
197
198 // Some children aren't added to an object until particular properties are read
199 // - e.g. child state objects aren't added until the 'states' property is read -
200 // but this should only affect internal objects that aren't shown by the
201 // debugger anyway.
202
203 QObjectList children = object->children();
204
205 int childrenCount = children.count();
206 for (int ii = 0; ii < children.count(); ++ii) {
207 if (qobject_cast<QDeclarativeContext*>(children[ii]) || QDeclarativeBoundSignal::cast(children[ii]))
208 --childrenCount;
209 }
210
211 message << childrenCount << recur;
212
213 QList<QDeclarativeObjectProperty> fakeProperties;
214
215 for (int ii = 0; ii < children.count(); ++ii) {
216 QObject *child = children.at(ii);
217 if (qobject_cast<QDeclarativeContext*>(child))
218 continue;
219 QDeclarativeBoundSignal *signal = QDeclarativeBoundSignal::cast(child);
220 if (signal) {
221 if (!dumpProperties)
222 continue;
223 QDeclarativeObjectProperty prop;
224 prop.type = QDeclarativeObjectProperty::SignalProperty;
225 prop.hasNotifySignal = false;
226 QDeclarativeExpression *expr = signal->expression();
227 if (expr) {
228 prop.value = expr->expression();
229 QObject *scope = expr->scopeObject();
230 if (scope) {
231 QString sig = QLatin1String(scope->metaObject()->method(signal->index()).signature());
232 int lparen = sig.indexOf(QLatin1Char('('));
233 if (lparen >= 0) {
234 QString methodName = sig.mid(0, lparen);
235 prop.name = QLatin1String("on") + methodName[0].toUpper()
236 + methodName.mid(1);
237 }
238 }
239 }
240 fakeProperties << prop;
241 } else {
242 if (recur)
243 buildObjectDump(message, child, recur, dumpProperties);
244 else
245 message << objectData(child);
246 }
247 }
248
249 if (!dumpProperties) {
250 message << 0;
251 return;
252 }
253
254 message << (object->metaObject()->propertyCount() + fakeProperties.count());
255
256 for (int ii = 0; ii < object->metaObject()->propertyCount(); ++ii)
257 message << propertyData(object, ii);
258
259 for (int ii = 0; ii < fakeProperties.count(); ++ii)
260 message << fakeProperties[ii];
261}
262
263void QDeclarativeEngineDebugServer::buildObjectList(QDataStream &message, QDeclarativeContext *ctxt)
264{
265 QDeclarativeContextData *p = QDeclarativeContextData::get(ctxt);
266
267 QString ctxtName = ctxt->objectName();
268 int ctxtId = QDeclarativeDebugService::idForObject(ctxt);
269
270 message << ctxtName << ctxtId;
271
272 int count = 0;
273
274 QDeclarativeContextData *child = p->childContexts;
275 while (child) {
276 ++count;
277 child = child->nextChild;
278 }
279
280 message << count;
281
282 child = p->childContexts;
283 while (child) {
284 buildObjectList(message, child->asQDeclarativeContext());
285 child = child->nextChild;
286 }
287
288 // Clean deleted objects
289 QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(ctxt);
290 for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) {
291 if (!ctxtPriv->instances.at(ii)) {
292 ctxtPriv->instances.removeAt(ii);
293 --ii;
294 }
295 }
296
297 message << ctxtPriv->instances.count();
298 for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) {
299 message << objectData(ctxtPriv->instances.at(ii));
300 }
301}
302
303QDeclarativeEngineDebugServer::QDeclarativeObjectData
304QDeclarativeEngineDebugServer::objectData(QObject *object)
305{
306 QDeclarativeData *ddata = QDeclarativeData::get(object);
307 QDeclarativeObjectData rv;
308 if (ddata && ddata->outerContext) {
309 rv.url = ddata->outerContext->url;
310 rv.lineNumber = ddata->lineNumber;
311 rv.columnNumber = ddata->columnNumber;
312 } else {
313 rv.lineNumber = -1;
314 rv.columnNumber = -1;
315 }
316
317 QDeclarativeContext *context = qmlContext(object);
318 if (context) {
319 QDeclarativeContextData *cdata = QDeclarativeContextData::get(context);
320 if (cdata)
321 rv.idString = cdata->findObjectId(object);
322 }
323
324 rv.objectName = object->objectName();
325 rv.objectId = QDeclarativeDebugService::idForObject(object);
326 rv.contextId = QDeclarativeDebugService::idForObject(qmlContext(object));
327
328 QDeclarativeType *type = QDeclarativeMetaType::qmlType(object->metaObject());
329 if (type) {
330 QString typeName = QLatin1String(type->qmlTypeName());
331 int lastSlash = typeName.lastIndexOf(QLatin1Char('/'));
332 rv.objectType = lastSlash < 0 ? typeName : typeName.mid(lastSlash+1);
333 } else {
334 rv.objectType = QString::fromUtf8(object->metaObject()->className());
335 int marker = rv.objectType.indexOf(QLatin1String("_QMLTYPE_"));
336 if (marker != -1)
337 rv.objectType = rv.objectType.left(marker);
338 }
339
340 return rv;
341}
342
343void QDeclarativeEngineDebugServer::messageReceived(const QByteArray &message)
344{
345 QDataStream ds(message);
346
347 QByteArray type;
348 ds >> type;
349
350 if (type == "LIST_ENGINES") {
351 int queryId;
352 ds >> queryId;
353
354 QByteArray reply;
355 QDataStream rs(&reply, QIODevice::WriteOnly);
356 rs << QByteArray("LIST_ENGINES_R");
357 rs << queryId << m_engines.count();
358
359 for (int ii = 0; ii < m_engines.count(); ++ii) {
360 QDeclarativeEngine *engine = m_engines.at(ii);
361
362 QString engineName = engine->objectName();
363 int engineId = QDeclarativeDebugService::idForObject(engine);
364
365 rs << engineName << engineId;
366 }
367
368 sendMessage(reply);
369 } else if (type == "LIST_OBJECTS") {
370 int queryId;
371 int engineId = -1;
372 ds >> queryId >> engineId;
373
374 QDeclarativeEngine *engine =
375 qobject_cast<QDeclarativeEngine *>(QDeclarativeDebugService::objectForId(engineId));
376
377 QByteArray reply;
378 QDataStream rs(&reply, QIODevice::WriteOnly);
379 rs << QByteArray("LIST_OBJECTS_R") << queryId;
380
381 if (engine)
382 buildObjectList(rs, engine->rootContext());
383
384 sendMessage(reply);
385 } else if (type == "FETCH_OBJECT") {
386 int queryId;
387 int objectId;
388 bool recurse;
389 bool dumpProperties = true;
390
391 ds >> queryId >> objectId >> recurse >> dumpProperties;
392
393 QObject *object = QDeclarativeDebugService::objectForId(objectId);
394
395 QByteArray reply;
396 QDataStream rs(&reply, QIODevice::WriteOnly);
397 rs << QByteArray("FETCH_OBJECT_R") << queryId;
398
399 if (object)
400 buildObjectDump(rs, object, recurse, dumpProperties);
401
402 sendMessage(reply);
403 } else if (type == "WATCH_OBJECT") {
404 int queryId;
405 int objectId;
406
407 ds >> queryId >> objectId;
408 bool ok = m_watch->addWatch(queryId, objectId);
409
410 QByteArray reply;
411 QDataStream rs(&reply, QIODevice::WriteOnly);
412 rs << QByteArray("WATCH_OBJECT_R") << queryId << ok;
413
414 sendMessage(reply);
415 } else if (type == "WATCH_PROPERTY") {
416 int queryId;
417 int objectId;
418 QByteArray property;
419
420 ds >> queryId >> objectId >> property;
421 bool ok = m_watch->addWatch(queryId, objectId, property);
422
423 QByteArray reply;
424 QDataStream rs(&reply, QIODevice::WriteOnly);
425 rs << QByteArray("WATCH_PROPERTY_R") << queryId << ok;
426
427 sendMessage(reply);
428 } else if (type == "WATCH_EXPR_OBJECT") {
429 int queryId;
430 int debugId;
431 QString expr;
432
433 ds >> queryId >> debugId >> expr;
434 bool ok = m_watch->addWatch(queryId, debugId, expr);
435
436 QByteArray reply;
437 QDataStream rs(&reply, QIODevice::WriteOnly);
438 rs << QByteArray("WATCH_EXPR_OBJECT_R") << queryId << ok;
439 sendMessage(reply);
440 } else if (type == "NO_WATCH") {
441 int queryId;
442
443 ds >> queryId;
444 m_watch->removeWatch(queryId);
445 } else if (type == "EVAL_EXPRESSION") {
446 int queryId;
447 int objectId;
448 QString expr;
449
450 ds >> queryId >> objectId >> expr;
451
452 QObject *object = QDeclarativeDebugService::objectForId(objectId);
453 QDeclarativeContext *context = qmlContext(object);
454 QVariant result;
455 if (object && context) {
456 QDeclarativeExpression exprObj(context, object, expr);
457 bool undefined = false;
458 QVariant value = exprObj.evaluate(&undefined);
459 if (undefined)
460 result = QLatin1String("<undefined>");
461 else
462 result = valueContents(value);
463 } else {
464 result = QLatin1String("<unknown context>");
465 }
466
467 QByteArray reply;
468 QDataStream rs(&reply, QIODevice::WriteOnly);
469 rs << QByteArray("EVAL_EXPRESSION_R") << queryId << result;
470
471 sendMessage(reply);
472 } else if (type == "SET_BINDING") {
473 int objectId;
474 QString propertyName;
475 QVariant expr;
476 bool isLiteralValue;
477 ds >> objectId >> propertyName >> expr >> isLiteralValue;
478 setBinding(objectId, propertyName, expr, isLiteralValue);
479 } else if (type == "RESET_BINDING") {
480 int objectId;
481 QString propertyName;
482 ds >> objectId >> propertyName;
483 resetBinding(objectId, propertyName);
484 } else if (type == "SET_METHOD_BODY") {
485 int objectId;
486 QString methodName;
487 QString methodBody;
488 ds >> objectId >> methodName >> methodBody;
489 setMethodBody(objectId, methodName, methodBody);
490 }
491}
492
493void QDeclarativeEngineDebugServer::setBinding(int objectId,
494 const QString &propertyName,
495 const QVariant &expression,
496 bool isLiteralValue)
497{
498 QObject *object = objectForId(objectId);
499 QDeclarativeContext *context = qmlContext(object);
500
501 if (object && context) {
502
503 QDeclarativeProperty property(object, propertyName, context);
504 if (isLiteralValue) {
505 property.write(expression);
506 } else if (hasValidSignal(object, propertyName)) {
507 QDeclarativeExpression *declarativeExpression = new QDeclarativeExpression(context, object, expression.toString());
508 QDeclarativeExpression *oldExpression = QDeclarativePropertyPrivate::setSignalExpression(property, declarativeExpression);
509 declarativeExpression->setSourceLocation(oldExpression->sourceFile(), oldExpression->lineNumber());
510 } else if (property.isProperty()) {
511 QDeclarativeBinding *binding = new QDeclarativeBinding(expression.toString(), object, context);
512 binding->setTarget(property);
513 binding->setNotifyOnValueChanged(true);
514 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::setBinding(property, binding);
515 if (oldBinding)
516 oldBinding->destroy();
517 binding->update();
518 } else {
519 qWarning() << "QDeclarativeEngineDebugServer::setBinding: unable to set property" << propertyName << "on object" << object;
520 }
521 }
522}
523
524void QDeclarativeEngineDebugServer::resetBinding(int objectId, const QString &propertyName)
525{
526 QObject *object = objectForId(objectId);
527 QDeclarativeContext *context = qmlContext(object);
528
529 if (object && context) {
530 if (object->property(propertyName.toLatin1()).isValid()) {
531 QDeclarativeProperty property(object, propertyName);
532 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(property);
533 if (oldBinding) {
534 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::setBinding(property, 0);
535 if (oldBinding)
536 oldBinding->destroy();
537 } else {
538 if (property.isResettable()) {
539 property.reset();
540 }
541 }
542 }
543 }
544}
545
546void QDeclarativeEngineDebugServer::setMethodBody(int objectId, const QString &method, const QString &body)
547{
548 QObject *object = objectForId(objectId);
549 QDeclarativeContext *context = qmlContext(object);
550 if (!object || !context || !context->engine())
551 return;
552 QDeclarativeContextData *contextData = QDeclarativeContextData::get(context);
553 if (!contextData)
554 return;
555
556 QDeclarativePropertyCache::Data dummy;
557 QDeclarativePropertyCache::Data *prop =
558 QDeclarativePropertyCache::property(context->engine(), object, method, dummy);
559
560 if (!prop || !(prop->flags & QDeclarativePropertyCache::Data::IsVMEFunction))
561 return;
562
563 QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex);
564 QList<QByteArray> paramNames = metaMethod.parameterNames();
565
566 QString paramStr;
567 for (int ii = 0; ii < paramNames.count(); ++ii) {
568 if (ii != 0) paramStr.append(QLatin1String(","));
569 paramStr.append(QString::fromUtf8(paramNames.at(ii)));
570 }
571
572 QString jsfunction = QLatin1String("(function ") + method + QLatin1String("(") + paramStr +
573 QLatin1String(") {");
574 jsfunction += body;
575 jsfunction += QLatin1String("\n})");
576
577 QDeclarativeVMEMetaObject *vmeMetaObject =
578 static_cast<QDeclarativeVMEMetaObject*>(QObjectPrivate::get(object)->metaObject);
579 Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this
580
581 int lineNumber = vmeMetaObject->vmeMethodLineNumber(prop->coreIndex);
582 vmeMetaObject->setVmeMethod(prop->coreIndex, QDeclarativeExpressionPrivate::evalInObjectScope(contextData, object, jsfunction, contextData->url.toString(), lineNumber, 0));
583}
584
585void QDeclarativeEngineDebugServer::propertyChanged(int id, int objectId, const QMetaProperty &property, const QVariant &value)
586{
587 QByteArray reply;
588 QDataStream rs(&reply, QIODevice::WriteOnly);
589
590 rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value);
591
592 sendMessage(reply);
593}
594
595void QDeclarativeEngineDebugServer::addEngine(QDeclarativeEngine *engine)
596{
597 Q_ASSERT(engine);
598 Q_ASSERT(!m_engines.contains(engine));
599
600 m_engines.append(engine);
601}
602
603void QDeclarativeEngineDebugServer::remEngine(QDeclarativeEngine *engine)
604{
605 Q_ASSERT(engine);
606 Q_ASSERT(m_engines.contains(engine));
607
608 m_engines.removeAll(engine);
609}
610
611void QDeclarativeEngineDebugServer::objectCreated(QDeclarativeEngine *engine, QObject *object)
612{
613 Q_ASSERT(engine);
614 Q_ASSERT(m_engines.contains(engine));
615
616 int engineId = QDeclarativeDebugService::idForObject(engine);
617 int objectId = QDeclarativeDebugService::idForObject(object);
618
619 QByteArray reply;
620 QDataStream rs(&reply, QIODevice::WriteOnly);
621
622 rs << QByteArray("OBJECT_CREATED") << engineId << objectId;
623 sendMessage(reply);
624}
625
626QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.