Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlenginedebugservice.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include "qqmlwatcher.h"
6
7#include <private/qqmldebugstatesdelegate_p.h>
8#include <private/qqmlboundsignal_p.h>
9#include <qqmlengine.h>
10#include <private/qqmlmetatype_p.h>
11#include <qqmlproperty.h>
12#include <private/qqmlproperty_p.h>
13#include <private/qqmlbinding_p.h>
14#include <private/qqmlcontext_p.h>
15#include <private/qqmlvaluetype_p.h>
16#include <private/qqmlvmemetaobject_p.h>
17#include <private/qqmlexpression_p.h>
18#include <private/qqmlsignalnames_p.h>
19
20#include <QtCore/qdebug.h>
21#include <QtCore/qmetaobject.h>
22#include <QtCore/qfileinfo.h>
23#include <QtCore/qjsonvalue.h>
24#include <QtCore/qjsonobject.h>
25#include <QtCore/qjsonarray.h>
26#include <QtCore/qjsondocument.h>
27
28#include <private/qmetaobject_p.h>
29#include <private/qqmldebugconnector_p.h>
30#include <private/qversionedpacket_p.h>
31
33
34using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
35
36class NullDevice : public QIODevice
37{
38public:
39 NullDevice() { open(QIODevice::ReadWrite); }
40
41protected:
42 qint64 readData(char *data, qint64 maxlen) final;
43 qint64 writeData(const char *data, qint64 len) final;
44};
45
46qint64 NullDevice::readData(char *data, qint64 maxlen)
47{
48 Q_UNUSED(data);
49 return maxlen;
50}
51
52qint64 NullDevice::writeData(const char *data, qint64 len)
53{
54 Q_UNUSED(data);
55 return len;
56}
57
58// check whether the data can be saved
59// (otherwise we assert in QVariant::operator<< when actually saving it)
60static bool isSaveable(const QVariant &value)
61{
62 const int valType = static_cast<int>(value.userType());
63 if (valType >= QMetaType::User)
64 return false;
65 NullDevice nullDevice;
66 QDataStream fakeStream(&nullDevice);
67 return QMetaType(valType).save(fakeStream, value.constData());
68}
69
70QQmlEngineDebugServiceImpl::QQmlEngineDebugServiceImpl(QObject *parent) :
71 QQmlEngineDebugService(2, parent), m_watch(new QQmlWatcher(this)), m_statesDelegate(nullptr)
72{
73 connect(m_watch, &QQmlWatcher::propertyChanged,
74 this, &QQmlEngineDebugServiceImpl::propertyChanged);
75
76 // Move the message into the correct thread for processing
77 connect(this, &QQmlEngineDebugServiceImpl::scheduleMessage,
78 this, &QQmlEngineDebugServiceImpl::processMessage, Qt::QueuedConnection);
79}
80
82{
83 delete m_statesDelegate;
84}
85
86QDataStream &operator<<(QDataStream &ds,
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
95QDataStream &operator>>(QDataStream &ds,
97{
98 ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString
99 >> data.objectName >> data.objectType >> data.objectId >> data.contextId
100 >> data.parentId;
101 return ds;
102}
103
104QDataStream &operator<<(QDataStream &ds,
106{
107 ds << (int)data.type << data.name;
108 ds << (isSaveable(data.value) ? data.value : QVariant());
109 ds << data.valueTypeName << data.binding << data.hasNotifySignal;
110 return ds;
111}
112
113QDataStream &operator>>(QDataStream &ds,
115{
116 int type;
117 ds >> type >> data.name >> data.value >> data.valueTypeName
118 >> data.binding >> data.hasNotifySignal;
120 return ds;
121}
122
123static bool hasValidSignal(QObject *object, const QString &propertyName)
124{
125 auto signalName = QQmlSignalNames::handlerNameToSignalName(propertyName);
126 if (!signalName)
127 return false;
128
129 int sigIdx = QQmlPropertyPrivate::findSignalByName(object->metaObject(), signalName->toLatin1())
130 .methodIndex();
131
132 if (sigIdx == -1)
133 return false;
134
135 return true;
136}
137
139QQmlEngineDebugServiceImpl::propertyData(QObject *obj, int propIdx)
140{
142
143 QMetaProperty prop = obj->metaObject()->property(propIdx);
144
146 rv.valueTypeName = QString::fromUtf8(prop.typeName());
147 rv.name = QString::fromUtf8(prop.name());
148 rv.hasNotifySignal = prop.hasNotifySignal();
149 QQmlAbstractBinding *binding =
150 QQmlPropertyPrivate::binding(QQmlProperty(obj, rv.name));
151 if (binding)
152 rv.binding = binding->expression();
153
154 rv.value = valueContents(prop.read(obj));
155
156 if (prop.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
158 } else if (QQmlMetaType::isList(prop.metaType())) {
160 } else if (prop.userType() == QMetaType::QVariant) {
162 } else if (rv.value.isValid()) {
164 }
165
166 return rv;
167}
168
169QVariant QQmlEngineDebugServiceImpl::valueContents(QVariant value) const
170{
171 // We can't send JS objects across the wire, so transform them to variant
172 // maps for serialization.
173 if (value.userType() == qMetaTypeId<QJSValue>())
174 value = value.value<QJSValue>().toVariant();
175 const QMetaType metaType = value.metaType();
176 const int metaTypeId = metaType.id();
177
178 //QObject * is not streamable.
179 //Convert all such instances to a String value
180
181 if (value.userType() == QMetaType::QVariantList) {
182 QVariantList contents;
183 QVariantList list = value.toList();
184 int count = list.size();
185 contents.reserve(count);
186 for (int i = 0; i < count; i++)
187 contents << valueContents(list.at(i));
188 return contents;
189 }
190
191 if (value.userType() == QMetaType::QVariantMap) {
192 QVariantMap contents;
193 const auto map = value.toMap();
194 for (auto i = map.cbegin(), end = map.cend(); i != end; ++i)
195 contents.insert(i.key(), valueContents(i.value()));
196 return contents;
197 }
198
199 switch (metaTypeId) {
200 case QMetaType::QRect:
201 case QMetaType::QRectF:
202 case QMetaType::QPoint:
203 case QMetaType::QPointF:
204 case QMetaType::QSize:
205 case QMetaType::QSizeF:
206 case QMetaType::QFont:
207 // Don't call the toString() method on those. The stream operators are better.
208 return value;
209 case QMetaType::QJsonValue:
210 return value.toJsonValue().toVariant();
211 case QMetaType::QJsonObject:
212 return value.toJsonObject().toVariantMap();
213 case QMetaType::QJsonArray:
214 return value.toJsonArray().toVariantList();
215 case QMetaType::QJsonDocument:
216 return value.toJsonDocument().toVariant();
217 default:
218 if (QQmlMetaType::isValueType(metaType)) {
219 const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(metaType);
220 if (mo) {
221 int toStringIndex = mo->indexOfMethod("toString()");
222 if (toStringIndex != -1) {
223 QMetaMethod mm = mo->method(toStringIndex);
224 QString s;
225 if (mm.invokeOnGadget(value.data(), Q_RETURN_ARG(QString, s)))
226 return s;
227 }
228 }
229 }
230
231 if (isSaveable(value))
232 return value;
233 }
234
235 if (metaType.flags().testFlag(QMetaType::PointerToQObject)) {
236 QObject *o = QQmlMetaType::toQObject(value);
237 if (o) {
238 QString name = o->objectName();
239 if (name.isEmpty())
240 name = QStringLiteral("<unnamed object>");
241 return name;
242 }
243 }
244
245 return QString(QStringLiteral("<unknown value>"));
246}
247
248void QQmlEngineDebugServiceImpl::buildObjectDump(QDataStream &message,
249 QObject *object, bool recur, bool dumpProperties)
250{
251 message << objectData(object);
252
253 QObjectList children = object->children();
254
255 int childrenCount = children.size();
256 for (int ii = 0; ii < children.size(); ++ii) {
257 if (qobject_cast<QQmlContext*>(children[ii]))
258 --childrenCount;
259 }
260
261 message << childrenCount << recur;
262
263 QList<QQmlObjectProperty> fakeProperties;
264
265 for (int ii = 0; ii < children.size(); ++ii) {
266 QObject *child = children.at(ii);
267 if (qobject_cast<QQmlContext*>(child))
268 continue;
269 if (recur)
270 buildObjectDump(message, child, recur, dumpProperties);
271 else
272 message << objectData(child);
273 }
274
275 if (!dumpProperties) {
276 message << 0;
277 return;
278 }
279
280 QList<int> propertyIndexes;
281 for (int ii = 0; ii < object->metaObject()->propertyCount(); ++ii) {
282 if (object->metaObject()->property(ii).isScriptable())
283 propertyIndexes << ii;
284 }
285
286 QQmlData *ddata = QQmlData::get(object);
287 if (ddata && ddata->signalHandlers) {
288 QQmlBoundSignal *signalHandler = ddata->signalHandlers;
289
290 while (signalHandler) {
293 prop.hasNotifySignal = false;
294 QQmlBoundSignalExpression *expr = signalHandler->expression();
295 if (expr) {
296 prop.value = expr->expression();
297 QObject *scope = expr->scopeObject();
298 if (scope) {
299 const QByteArray methodName = QMetaObjectPrivate::signal(scope->metaObject(),
300 signalHandler->signalIndex()).name();
301 if (!methodName.isEmpty()) {
302 prop.name = QQmlSignalNames::signalNameToHandlerName(methodName);
303 }
304 }
305 }
306 fakeProperties << prop;
307
308 signalHandler = nextSignal(signalHandler);
309 }
310 }
311
312 message << int(propertyIndexes.size() + fakeProperties.size());
313
314 for (int ii = 0; ii < propertyIndexes.size(); ++ii)
315 message << propertyData(object, propertyIndexes.at(ii));
316
317 for (int ii = 0; ii < fakeProperties.size(); ++ii)
318 message << fakeProperties[ii];
319}
320
321void QQmlEngineDebugServiceImpl::prepareDeferredObjects(QObject *obj)
322{
323 qmlExecuteDeferred(obj);
324
325 QObjectList children = obj->children();
326 for (int ii = 0; ii < children.size(); ++ii) {
327 QObject *child = children.at(ii);
328 prepareDeferredObjects(child);
329 }
330
331}
332
333void QQmlEngineDebugServiceImpl::storeObjectIds(QObject *co)
334{
335 QQmlDebugService::idForObject(co);
336 QObjectList children = co->children();
337 for (int ii = 0; ii < children.size(); ++ii)
338 storeObjectIds(children.at(ii));
339}
340
341void QQmlEngineDebugServiceImpl::buildObjectList(QDataStream &message,
342 QQmlContext *ctxt,
343 const QList<QPointer<QObject> > &instances)
344{
345 if (!ctxt->isValid())
346 return;
347
348 QQmlRefPointer<QQmlContextData> p = QQmlContextData::get(ctxt);
349
350 QString ctxtName = ctxt->objectName();
351 int ctxtId = QQmlDebugService::idForObject(ctxt);
352 if (ctxt->contextObject())
353 storeObjectIds(ctxt->contextObject());
354
355 message << ctxtName << ctxtId;
356
357 int count = 0;
358
359 QQmlRefPointer<QQmlContextData> child = p->childContexts();
360 while (child) {
361 ++count;
362 child = child->nextChild();
363 }
364
365 message << count;
366
367 child = p->childContexts();
368 while (child) {
369 buildObjectList(message, child->asQQmlContext(), instances);
370 child = child->nextChild();
371 }
372
373 count = 0;
374 for (int ii = 0; ii < instances.size(); ++ii) {
375 QQmlData *data = QQmlData::get(instances.at(ii));
376 if (data->context == p.data())
377 count ++;
378 }
379 message << count;
380
381 for (int ii = 0; ii < instances.size(); ++ii) {
382 QQmlData *data = QQmlData::get(instances.at(ii));
383 if (data->context == p.data())
384 message << objectData(instances.at(ii));
385 }
386}
387
388void QQmlEngineDebugServiceImpl::buildStatesList(bool cleanList,
389 const QList<QPointer<QObject> > &instances)
390{
391 if (auto delegate = statesDelegate())
392 delegate->buildStatesList(cleanList, instances);
393}
394
396QQmlEngineDebugServiceImpl::objectData(QObject *object)
397{
398 QQmlData *ddata = QQmlData::get(object);
400 if (ddata && ddata->outerContext) {
401 rv.url = ddata->outerContext->url();
402 rv.lineNumber = ddata->lineNumber;
403 rv.columnNumber = ddata->columnNumber;
404 } else {
405 rv.lineNumber = -1;
406 rv.columnNumber = -1;
407 }
408
409 QQmlContext *context = qmlContext(object);
410 if (context && context->isValid())
411 rv.idString = QQmlContextData::get(context)->findObjectId(object);
412
413 rv.objectName = object->objectName();
414 rv.objectId = QQmlDebugService::idForObject(object);
415 rv.contextId = QQmlDebugService::idForObject(qmlContext(object));
416 rv.parentId = QQmlDebugService::idForObject(object->parent());
417 rv.objectType = QQmlMetaType::prettyTypeName(object);
418 return rv;
419}
420
421void QQmlEngineDebugServiceImpl::messageReceived(const QByteArray &message)
422{
423 emit scheduleMessage(message);
424}
425
426/*!
427 Returns a list of objects matching the given filename, line and column.
428*/
429QList<QObject*> QQmlEngineDebugServiceImpl::objectForLocationInfo(const QString &filename,
430 int lineNumber, int columnNumber)
431{
432 QList<QObject *> objects;
433 const QHash<int, QObject *> &hash = objectsForIds();
434 for (QHash<int, QObject *>::ConstIterator i = hash.constBegin(); i != hash.constEnd(); ++i) {
435 QQmlData *ddata = QQmlData::get(i.value());
436 if (ddata && ddata->outerContext && ddata->outerContext->isValid()) {
437 if (QFileInfo(ddata->outerContext->urlString()).fileName() == filename &&
438 ddata->lineNumber == lineNumber &&
439 ddata->columnNumber >= columnNumber) {
440 objects << i.value();
441 }
442 }
443 }
444 return objects;
445}
446
447void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message)
448{
449 QQmlDebugPacket ds(message);
450
451 QByteArray type;
452 qint32 queryId;
453 ds >> type >> queryId;
454
455 QQmlDebugPacket rs;
456
457 if (type == "LIST_ENGINES") {
458 rs << QByteArray("LIST_ENGINES_R");
459 rs << queryId << int(m_engines.size());
460
461 for (int ii = 0; ii < m_engines.size(); ++ii) {
462 QJSEngine *engine = m_engines.at(ii);
463
464 QString engineName = engine->objectName();
465 qint32 engineId = QQmlDebugService::idForObject(engine);
466
467 rs << engineName << engineId;
468 }
469
470 } else if (type == "LIST_OBJECTS") {
471 qint32 engineId = -1;
472 ds >> engineId;
473
474 QQmlEngine *engine =
475 qobject_cast<QQmlEngine *>(QQmlDebugService::objectForId(engineId));
476
477 rs << QByteArray("LIST_OBJECTS_R") << queryId;
478
479 if (engine) {
480 QQmlContext *rootContext = engine->rootContext();
481 QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(rootContext);
482 ctxtPriv->cleanInstances(); // Clean deleted objects
483 const QList<QPointer<QObject>> instances = ctxtPriv->instances();
484 buildObjectList(rs, rootContext, instances);
485 buildStatesList(true, instances);
486 }
487
488 } else if (type == "FETCH_OBJECT") {
489 qint32 objectId;
490 bool recurse;
491 bool dumpProperties = true;
492
493 ds >> objectId >> recurse >> dumpProperties;
494
495 QObject *object = QQmlDebugService::objectForId(objectId);
496
497 rs << QByteArray("FETCH_OBJECT_R") << queryId;
498
499 if (object) {
500 if (recurse)
501 prepareDeferredObjects(object);
502 buildObjectDump(rs, object, recurse, dumpProperties);
503 }
504
505 } else if (type == "FETCH_OBJECTS_FOR_LOCATION") {
506 QString file;
507 qint32 lineNumber;
508 qint32 columnNumber;
509 bool recurse;
510 bool dumpProperties = true;
511
512 ds >> file >> lineNumber >> columnNumber >> recurse >> dumpProperties;
513
514 const QList<QObject*> objects = objectForLocationInfo(file, lineNumber, columnNumber);
515
516 rs << QByteArray("FETCH_OBJECTS_FOR_LOCATION_R") << queryId
517 << int(objects.size());
518
519 for (QObject *object : objects) {
520 if (recurse)
521 prepareDeferredObjects(object);
522 buildObjectDump(rs, object, recurse, dumpProperties);
523 }
524
525 } else if (type == "WATCH_OBJECT") {
526 qint32 objectId;
527
528 ds >> objectId;
529 bool ok = m_watch->addWatch(queryId, objectId);
530
531 rs << QByteArray("WATCH_OBJECT_R") << queryId << ok;
532
533 } else if (type == "WATCH_PROPERTY") {
534 qint32 objectId;
535 QByteArray property;
536
537 ds >> objectId >> property;
538 bool ok = m_watch->addWatch(queryId, objectId, property);
539
540 rs << QByteArray("WATCH_PROPERTY_R") << queryId << ok;
541
542 } else if (type == "WATCH_EXPR_OBJECT") {
543 qint32 debugId;
544 QString expr;
545
546 ds >> debugId >> expr;
547 bool ok = m_watch->addWatch(queryId, debugId, expr);
548
549 rs << QByteArray("WATCH_EXPR_OBJECT_R") << queryId << ok;
550
551 } else if (type == "NO_WATCH") {
552 bool ok = m_watch->removeWatch(queryId);
553
554 rs << QByteArray("NO_WATCH_R") << queryId << ok;
555
556 } else if (type == "EVAL_EXPRESSION") {
557 qint32 objectId;
558 QString expr;
559
560 ds >> objectId >> expr;
561 qint32 engineId = -1;
562 if (!ds.atEnd())
563 ds >> engineId;
564
565 QObject *object = QQmlDebugService::objectForId(objectId);
566 QQmlContext *context = qmlContext(object);
567 if (!context || !context->isValid()) {
568 QQmlEngine *engine = qobject_cast<QQmlEngine *>(
569 QQmlDebugService::objectForId(engineId));
570 if (engine && m_engines.contains(engine))
571 context = engine->rootContext();
572 }
573 QVariant result;
574 if (context && context->isValid()) {
575 QQmlExpression exprObj(context, object, expr);
576 bool undefined = false;
577 QVariant value = exprObj.evaluate(&undefined);
578 if (undefined)
579 result = QString(QStringLiteral("<undefined>"));
580 else
581 result = valueContents(value);
582 } else {
583 result = QString(QStringLiteral("<unknown context>"));
584 }
585
586 rs << QByteArray("EVAL_EXPRESSION_R") << queryId << result;
587
588 } else if (type == "SET_BINDING") {
589 qint32 objectId;
590 QString propertyName;
591 QVariant expr;
592 bool isLiteralValue;
593 QString filename;
594 qint32 line;
595 ds >> objectId >> propertyName >> expr >> isLiteralValue >>
596 filename >> line;
597 bool ok = setBinding(objectId, propertyName, expr, isLiteralValue,
598 filename, line);
599
600 rs << QByteArray("SET_BINDING_R") << queryId << ok;
601
602 } else if (type == "RESET_BINDING") {
603 qint32 objectId;
604 QString propertyName;
605 ds >> objectId >> propertyName;
606 bool ok = resetBinding(objectId, propertyName);
607
608 rs << QByteArray("RESET_BINDING_R") << queryId << ok;
609
610 } else if (type == "SET_METHOD_BODY") {
611 qint32 objectId;
612 QString methodName;
613 QString methodBody;
614 ds >> objectId >> methodName >> methodBody;
615 bool ok = setMethodBody(objectId, methodName, methodBody);
616
617 rs << QByteArray("SET_METHOD_BODY_R") << queryId << ok;
618
619 }
620 emit messageToClient(name(), rs.data());
621}
622
623bool QQmlEngineDebugServiceImpl::setBinding(int objectId,
624 const QString &propertyName,
625 const QVariant &expression,
626 bool isLiteralValue,
627 QString filename,
628 int line,
629 int column)
630{
631 bool ok = true;
632 QObject *object = objectForId(objectId);
633 QQmlContext *context = qmlContext(object);
634
635 if (object && context && context->isValid()) {
636 QQmlProperty property(object, propertyName, context);
637 if (property.isValid()) {
638
639 bool inBaseState = true;
640 if (auto delegate = statesDelegate()) {
641 delegate->updateBinding(context, property, expression, isLiteralValue,
642 filename, line, column, &inBaseState);
643 }
644
645 if (inBaseState) {
646 if (isLiteralValue) {
647 property.write(expression);
648 } else if (hasValidSignal(object, propertyName)) {
649 QQmlBoundSignalExpression *qmlExpression = new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(property)->signalIndex(),
650 QQmlContextData::get(context), object, expression.toString(),
651 filename, line, column);
652 QQmlPropertyPrivate::takeSignalExpression(property, qmlExpression);
653 } else if (property.isProperty()) {
654 QQmlBinding *binding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, expression.toString(), object, QQmlContextData::get(context), filename, line);
655 binding->setTarget(property);
656 QQmlPropertyPrivate::setBinding(binding);
657 binding->update();
658 } else {
659 ok = false;
660 qWarning() << "QQmlEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object;
661 }
662 }
663
664 } else {
665 // not a valid property
666 if (auto delegate = statesDelegate())
667 ok = delegate->setBindingForInvalidProperty(object, propertyName, expression, isLiteralValue);
668 if (!ok)
669 qWarning() << "QQmlEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object;
670 }
671 }
672 return ok;
673}
674
675bool QQmlEngineDebugServiceImpl::resetBinding(int objectId, const QString &propertyName)
676{
677 QObject *object = objectForId(objectId);
678 QQmlContext *context = qmlContext(object);
679
680 if (object && context && context->isValid()) {
681 QStringView parentPropertyRef(propertyName);
682 const int idx = parentPropertyRef.indexOf(QLatin1Char('.'));
683 if (idx != -1)
684 parentPropertyRef = parentPropertyRef.left(idx);
685
686 const QByteArray parentProperty = parentPropertyRef.toLatin1();
687 if (object->property(parentProperty).isValid()) {
688 QQmlProperty property(object, propertyName);
689 QQmlPropertyPrivate::removeBinding(property);
690 if (property.isResettable()) {
691 // Note: this will reset the property in any case, without regard to states
692 // Right now almost no QQuickItem has reset methods for its properties (with the
693 // notable exception of QQuickAnchors), so this is not a big issue
694 // later on, setBinding does take states into account
695 property.reset();
696 } else {
697 // overwrite with default value
698 QQmlType objType = QQmlMetaType::qmlType(object->metaObject());
699 if (objType.isValid()) {
700 if (QObject *emptyObject = objType.create()) {
701 if (emptyObject->property(parentProperty).isValid()) {
702 QVariant defaultValue = QQmlProperty(emptyObject, propertyName).read();
703 if (defaultValue.isValid()) {
704 setBinding(objectId, propertyName, defaultValue, true);
705 }
706 }
707 delete emptyObject;
708 }
709 }
710 }
711 return true;
712 }
713
714 if (hasValidSignal(object, propertyName)) {
715 QQmlProperty property(object, propertyName, context);
716 QQmlPropertyPrivate::setSignalExpression(property, nullptr);
717 return true;
718 }
719
720 if (auto delegate = statesDelegate()) {
721 delegate->resetBindingForInvalidProperty(object, propertyName);
722 return true;
723 }
724
725 return false;
726 }
727 // object or context null.
728 return false;
729}
730
731bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &method, const QString &body)
732{
733 QObject *object = objectForId(objectId);
734 QQmlContext *context = qmlContext(object);
735 if (!object || !context || !context->isValid())
736 return false;
737 QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context);
738
739 QQmlPropertyData dummy;
740 const QQmlPropertyData *prop = QQmlPropertyCache::property(object, method, contextData, &dummy);
741
742 if (!prop || !prop->isVMEFunction())
743 return false;
744
745 QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex());
746 QList<QByteArray> paramNames = metaMethod.parameterNames();
747
748 QString paramStr;
749 for (int ii = 0; ii < paramNames.size(); ++ii) {
750 if (ii != 0) paramStr.append(QLatin1Char(','));
751 paramStr.append(QString::fromUtf8(paramNames.at(ii)));
752 }
753
754 const QString jsfunction = QLatin1String("(function ") + method + QLatin1Char('(') + paramStr +
755 QLatin1String(") {") + body + QLatin1String("\n})");
756
757 QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(object);
758 Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this
759
760 QV4::ExecutionEngine *v4 = qmlEngine(object)->handle();
761 QV4::Scope scope(v4);
762
763 int lineNumber = 0;
764 QV4::Scoped<QV4::JavaScriptFunctionObject> oldMethod(
765 scope, vmeMetaObject->vmeMethod(prop->coreIndex()));
766 if (oldMethod && oldMethod->d()->function)
767 lineNumber = oldMethod->d()->function->compiledFunction->location.line();
768
769 QV4::ScopedValue v(scope, QQmlJavaScriptExpression::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber));
770 vmeMetaObject->setVmeMethod(prop->coreIndex(), v);
771 return true;
772}
773
774void QQmlEngineDebugServiceImpl::propertyChanged(
775 qint32 id, qint32 objectId, const QMetaProperty &property, const QVariant &value)
776{
777 QQmlDebugPacket rs;
778 rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value);
779 emit messageToClient(name(), rs.data());
780}
781
783{
784 Q_ASSERT(engine);
785 Q_ASSERT(!m_engines.contains(engine));
786
787 m_engines.append(engine);
788 emit attachedToEngine(engine);
789}
790
792{
793 Q_ASSERT(engine);
794 Q_ASSERT(m_engines.contains(engine));
795
796 m_engines.removeAll(engine);
797 emit detachedFromEngine(engine);
798}
799
800void QQmlEngineDebugServiceImpl::objectCreated(QJSEngine *engine, QObject *object)
801{
802 Q_ASSERT(engine);
803 if (!m_engines.contains(engine))
804 return;
805
806 qint32 engineId = QQmlDebugService::idForObject(engine);
807 qint32 objectId = QQmlDebugService::idForObject(object);
808 qint32 parentId = QQmlDebugService::idForObject(object->parent());
809
810 QQmlDebugPacket rs;
811
812 //unique queryId -1
813 rs << QByteArray("OBJECT_CREATED") << qint32(-1) << engineId << objectId << parentId;
814 emit messageToClient(name(), rs.data());
815}
816
817QT_END_NAMESPACE
818
819#include "moc_qqmlenginedebugservice.cpp"
qint64 writeData(const char *data, qint64 len) final
Writes up to maxSize bytes from data to the device.
qint64 readData(char *data, qint64 maxlen) final
Reads up to maxSize bytes from the device into data, and returns the number of bytes read or -1 if an...
void engineAboutToBeAdded(QJSEngine *) override
void messageReceived(const QByteArray &) override
void engineAboutToBeRemoved(QJSEngine *) override
QQmlDebugStatesDelegate * statesDelegate()
The QQmlProperty class abstracts accessing properties on objects created from QML.
bool removeWatch(int id)
Combined button and popup list for selecting options.
static bool isSaveable(const QVariant &value)
static bool hasValidSignal(QObject *object, const QString &propertyName)
QDataStream & operator>>(QDataStream &, QQmlEngineDebugServiceImpl::QQmlObjectProperty &)
QDataStream & operator>>(QDataStream &, QQmlEngineDebugServiceImpl::QQmlObjectData &)