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

source code of qtdeclarative/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp