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// Qt-Security score:significant
4
6#include "qqmlwatcher.h"
7
8#include <private/qqmldebugstatesdelegate_p.h>
9#include <private/qqmlboundsignal_p.h>
10#include <qqmlengine.h>
11#include <private/qqmlmetatype_p.h>
12#include <qqmlproperty.h>
13#include <private/qqmlproperty_p.h>
14#include <private/qqmlbinding_p.h>
15#include <private/qqmlcontext_p.h>
16#include <private/qqmlvaluetype_p.h>
17#include <private/qqmlvmemetaobject_p.h>
18#include <private/qqmlexpression_p.h>
19#include <private/qqmlsignalnames_p.h>
20
21#include <QtCore/qdebug.h>
22#include <QtCore/qmetaobject.h>
23#include <QtCore/qfileinfo.h>
24#include <QtCore/qjsonvalue.h>
25#include <QtCore/qjsonobject.h>
26#include <QtCore/qjsonarray.h>
27#include <QtCore/qjsondocument.h>
28
29#include <private/qmetaobject_p.h>
30#include <private/qqmldebugconnector_p.h>
31#include <private/qversionedpacket_p.h>
32
34
35using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
36
37class NullDevice : public QIODevice
38{
39public:
40 NullDevice() { open(QIODevice::ReadWrite); }
41
42protected:
43 qint64 readData(char *data, qint64 maxlen) final;
44 qint64 writeData(const char *data, qint64 len) final;
45};
46
47qint64 NullDevice::readData(char *data, qint64 maxlen)
48{
49 Q_UNUSED(data);
50 return maxlen;
51}
52
53qint64 NullDevice::writeData(const char *data, qint64 len)
54{
55 Q_UNUSED(data);
56 return len;
57}
58
59// check whether the data can be saved
60// (otherwise we assert in QVariant::operator<< when actually saving it)
61static bool isSaveable(const QVariant &value)
62{
63 const int valType = static_cast<int>(value.userType());
64 if (valType >= QMetaType::User)
65 return false;
66 NullDevice nullDevice;
67 QDataStream fakeStream(&nullDevice);
68 return QMetaType(valType).save(fakeStream, value.constData());
69}
70
71QQmlEngineDebugServiceImpl::QQmlEngineDebugServiceImpl(QObject *parent) :
72 QQmlEngineDebugService(2, parent), m_watch(new QQmlWatcher(this)), m_statesDelegate(nullptr)
73{
74 connect(m_watch, &QQmlWatcher::propertyChanged,
75 this, &QQmlEngineDebugServiceImpl::propertyChanged);
76
77 // Move the message into the correct thread for processing
78 connect(this, &QQmlEngineDebugServiceImpl::scheduleMessage,
79 this, &QQmlEngineDebugServiceImpl::processMessage, Qt::QueuedConnection);
80}
81
83{
84 delete m_statesDelegate;
85}
86
87QDataStream &operator<<(QDataStream &ds,
89{
90 ds << data.url << data.lineNumber << data.columnNumber << data.idString
91 << data.objectName << data.objectType << data.objectId << data.contextId
92 << data.parentId;
93 return ds;
94}
95
96QDataStream &operator>>(QDataStream &ds,
98{
99 ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString
100 >> data.objectName >> data.objectType >> data.objectId >> data.contextId
101 >> data.parentId;
102 return ds;
103}
104
105QDataStream &operator<<(QDataStream &ds,
107{
108 ds << (int)data.type << data.name;
109 ds << (isSaveable(data.value) ? data.value : QVariant());
110 ds << data.valueTypeName << data.binding << data.hasNotifySignal;
111 return ds;
112}
113
114QDataStream &operator>>(QDataStream &ds,
116{
117 int type;
118 ds >> type >> data.name >> data.value >> data.valueTypeName
119 >> data.binding >> data.hasNotifySignal;
121 return ds;
122}
123
124static bool hasValidSignal(QObject *object, const QString &propertyName)
125{
126 auto signalName = QQmlSignalNames::handlerNameToSignalName(propertyName);
127 if (!signalName)
128 return false;
129
130 int sigIdx = QQmlPropertyPrivate::findSignalByName(object->metaObject(), signalName->toLatin1())
131 .methodIndex();
132
133 if (sigIdx == -1)
134 return false;
135
136 return true;
137}
138
140QQmlEngineDebugServiceImpl::propertyData(QObject *obj, int propIdx)
141{
143
144 QMetaProperty prop = obj->metaObject()->property(propIdx);
145
147 rv.valueTypeName = QString::fromUtf8(prop.typeName());
148 rv.name = QString::fromUtf8(prop.name());
149 rv.hasNotifySignal = prop.hasNotifySignal();
150 QQmlAbstractBinding *binding =
151 QQmlPropertyPrivate::binding(QQmlProperty(obj, rv.name));
152 if (binding)
153 rv.binding = binding->expression();
154
155 rv.value = valueContents(prop.read(obj));
156
157 if (prop.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
159 } else if (QQmlMetaType::isList(prop.metaType())) {
161 } else if (prop.userType() == QMetaType::QVariant) {
163 } else if (rv.value.isValid()) {
165 }
166
167 return rv;
168}
169
170QVariant QQmlEngineDebugServiceImpl::valueContents(QVariant value) const
171{
172 // We can't send JS objects across the wire, so transform them to variant
173 // maps for serialization.
174 if (value.userType() == qMetaTypeId<QJSValue>())
175 value = value.value<QJSValue>().toVariant();
176 const QMetaType metaType = value.metaType();
177 const int metaTypeId = metaType.id();
178
179 //QObject * is not streamable.
180 //Convert all such instances to a String value
181
182 if (value.userType() == QMetaType::QVariantList) {
183 QVariantList contents;
184 QVariantList list = value.toList();
185 int count = list.size();
186 contents.reserve(count);
187 for (int i = 0; i < count; i++)
188 contents << valueContents(list.at(i));
189 return contents;
190 }
191
192 if (value.userType() == QMetaType::QVariantMap) {
193 QVariantMap contents;
194 const auto map = value.toMap();
195 for (auto i = map.cbegin(), end = map.cend(); i != end; ++i)
196 contents.insert(i.key(), valueContents(i.value()));
197 return contents;
198 }
199
200 switch (metaTypeId) {
201 case QMetaType::QRect:
202 case QMetaType::QRectF:
203 case QMetaType::QPoint:
204 case QMetaType::QPointF:
205 case QMetaType::QSize:
206 case QMetaType::QSizeF:
207 case QMetaType::QFont:
208 // Don't call the toString() method on those. The stream operators are better.
209 return value;
210 case QMetaType::QJsonValue:
211 return value.toJsonValue().toVariant();
212 case QMetaType::QJsonObject:
213 return value.toJsonObject().toVariantMap();
214 case QMetaType::QJsonArray:
215 return value.toJsonArray().toVariantList();
216 case QMetaType::QJsonDocument:
217 return value.toJsonDocument().toVariant();
218 default:
219 if (QQmlMetaType::isValueType(metaType)) {
220 const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(metaType);
221 if (mo) {
222 int toStringIndex = mo->indexOfMethod("toString()");
223 if (toStringIndex != -1) {
224 QMetaMethod mm = mo->method(toStringIndex);
225 QString s;
226 if (mm.invokeOnGadget(value.data(), Q_RETURN_ARG(QString, s)))
227 return s;
228 }
229 }
230 }
231
232 if (isSaveable(value))
233 return value;
234 }
235
236 if (metaType.flags().testFlag(QMetaType::PointerToQObject)) {
237 QObject *o = QQmlMetaType::toQObject(value);
238 if (o) {
239 QString name = o->objectName();
240 if (name.isEmpty())
241 name = QStringLiteral("<unnamed object>");
242 return name;
243 }
244 }
245
246 return QString(QStringLiteral("<unknown value>"));
247}
248
249void QQmlEngineDebugServiceImpl::buildObjectDump(QDataStream &message,
250 QObject *object, bool recur, bool dumpProperties)
251{
252 message << objectData(object);
253
254 QObjectList children = object->children();
255
256 int childrenCount = children.size();
257 for (int ii = 0; ii < children.size(); ++ii) {
258 if (qobject_cast<QQmlContext*>(children[ii]))
259 --childrenCount;
260 }
261
262 message << childrenCount << recur;
263
264 QList<QQmlObjectProperty> fakeProperties;
265
266 for (int ii = 0; ii < children.size(); ++ii) {
267 QObject *child = children.at(ii);
268 if (qobject_cast<QQmlContext*>(child))
269 continue;
270 if (recur)
271 buildObjectDump(message, child, recur, dumpProperties);
272 else
273 message << objectData(child);
274 }
275
276 if (!dumpProperties) {
277 message << 0;
278 return;
279 }
280
281 QList<int> propertyIndexes;
282 for (int ii = 0; ii < object->metaObject()->propertyCount(); ++ii) {
283 if (object->metaObject()->property(ii).isScriptable())
284 propertyIndexes << ii;
285 }
286
287 QQmlData *ddata = QQmlData::get(object);
288 if (ddata && ddata->signalHandlers) {
289 QQmlBoundSignal *signalHandler = ddata->signalHandlers;
290
291 while (signalHandler) {
294 prop.hasNotifySignal = false;
295 QQmlBoundSignalExpression *expr = signalHandler->expression();
296 if (expr) {
297 prop.value = expr->expression();
298 QObject *scope = expr->scopeObject();
299 if (scope) {
300 const QByteArray methodName = QMetaObjectPrivate::signal(scope->metaObject(),
301 signalHandler->signalIndex()).name();
302 if (!methodName.isEmpty()) {
303 prop.name = QQmlSignalNames::signalNameToHandlerName(methodName);
304 }
305 }
306 }
307 fakeProperties << prop;
308
309 signalHandler = nextSignal(signalHandler);
310 }
311 }
312
313 message << int(propertyIndexes.size() + fakeProperties.size());
314
315 for (int ii = 0; ii < propertyIndexes.size(); ++ii)
316 message << propertyData(object, propertyIndexes.at(ii));
317
318 for (int ii = 0; ii < fakeProperties.size(); ++ii)
319 message << fakeProperties[ii];
320}
321
322void QQmlEngineDebugServiceImpl::prepareDeferredObjects(QObject *obj)
323{
324 qmlExecuteDeferred(obj);
325
326 QObjectList children = obj->children();
327 for (int ii = 0; ii < children.size(); ++ii) {
328 QObject *child = children.at(ii);
329 prepareDeferredObjects(child);
330 }
331
332}
333
334void QQmlEngineDebugServiceImpl::storeObjectIds(QObject *co)
335{
336 QQmlDebugService::idForObject(co);
337 QObjectList children = co->children();
338 for (int ii = 0; ii < children.size(); ++ii)
339 storeObjectIds(children.at(ii));
340}
341
342void QQmlEngineDebugServiceImpl::buildObjectList(QDataStream &message,
343 QQmlContext *ctxt,
344 const QList<QPointer<QObject> > &instances)
345{
346 if (!ctxt->isValid())
347 return;
348
349 QQmlRefPointer<QQmlContextData> p = QQmlContextData::get(ctxt);
350
351 QString ctxtName = ctxt->objectName();
352 int ctxtId = QQmlDebugService::idForObject(ctxt);
353 if (ctxt->contextObject())
354 storeObjectIds(ctxt->contextObject());
355
356 message << ctxtName << ctxtId;
357
358 int count = 0;
359
360 QQmlRefPointer<QQmlContextData> child = p->childContexts();
361 while (child) {
362 ++count;
363 child = child->nextChild();
364 }
365
366 message << count;
367
368 child = p->childContexts();
369 while (child) {
370 buildObjectList(message, child->asQQmlContext(), instances);
371 child = child->nextChild();
372 }
373
374 count = 0;
375 for (int ii = 0; ii < instances.size(); ++ii) {
376 QQmlData *data = QQmlData::get(instances.at(ii));
377 if (data->context == p.data())
378 count ++;
379 }
380 message << count;
381
382 for (int ii = 0; ii < instances.size(); ++ii) {
383 QQmlData *data = QQmlData::get(instances.at(ii));
384 if (data->context == p.data())
385 message << objectData(instances.at(ii));
386 }
387}
388
389void QQmlEngineDebugServiceImpl::buildStatesList(bool cleanList,
390 const QList<QPointer<QObject> > &instances)
391{
392 if (auto delegate = statesDelegate())
393 delegate->buildStatesList(cleanList, instances);
394}
395
397QQmlEngineDebugServiceImpl::objectData(QObject *object)
398{
399 QQmlData *ddata = QQmlData::get(object);
401 if (ddata && ddata->outerContext) {
402 rv.url = ddata->outerContext->url();
403 rv.lineNumber = ddata->lineNumber;
404 rv.columnNumber = ddata->columnNumber;
405 } else {
406 rv.lineNumber = -1;
407 rv.columnNumber = -1;
408 }
409
410 QQmlContext *context = qmlContext(object);
411 if (context && context->isValid())
412 rv.idString = QQmlContextData::get(context)->findObjectId(object);
413
414 rv.objectName = object->objectName();
415 rv.objectId = QQmlDebugService::idForObject(object);
416 rv.contextId = QQmlDebugService::idForObject(qmlContext(object));
417 rv.parentId = QQmlDebugService::idForObject(object->parent());
418 rv.objectType = QQmlMetaType::prettyTypeName(object);
419 return rv;
420}
421
422void QQmlEngineDebugServiceImpl::messageReceived(const QByteArray &message)
423{
424 emit scheduleMessage(message);
425}
426
427/*!
428 Returns a list of objects matching the given filename, line and column.
429*/
430QList<QObject*> QQmlEngineDebugServiceImpl::objectForLocationInfo(const QString &filename,
431 int lineNumber, int columnNumber)
432{
433 QList<QObject *> objects;
434 const QHash<int, QObject *> &hash = objectsForIds();
435 for (QHash<int, QObject *>::ConstIterator i = hash.constBegin(); i != hash.constEnd(); ++i) {
436 QQmlData *ddata = QQmlData::get(i.value());
437 if (ddata && ddata->outerContext && ddata->outerContext->isValid()) {
438 if (QFileInfo(ddata->outerContext->urlString()).fileName() == filename &&
439 ddata->lineNumber == lineNumber &&
440 ddata->columnNumber >= columnNumber) {
441 objects << i.value();
442 }
443 }
444 }
445 return objects;
446}
447
448void QQmlEngineDebugServiceImpl::processMessage(const QByteArray &message)
449{
450 QQmlDebugPacket ds(message);
451
452 QByteArray type;
453 qint32 queryId;
454 ds >> type >> queryId;
455
456 QQmlDebugPacket rs;
457
458 if (type == "LIST_ENGINES") {
459 rs << QByteArray("LIST_ENGINES_R");
460 rs << queryId << int(m_engines.size());
461
462 for (int ii = 0; ii < m_engines.size(); ++ii) {
463 QJSEngine *engine = m_engines.at(ii);
464
465 QString engineName = engine->objectName();
466 qint32 engineId = QQmlDebugService::idForObject(engine);
467
468 rs << engineName << engineId;
469 }
470
471 } else if (type == "LIST_OBJECTS") {
472 qint32 engineId = -1;
473 ds >> engineId;
474
475 QQmlEngine *engine =
476 qobject_cast<QQmlEngine *>(QQmlDebugService::objectForId(engineId));
477
478 rs << QByteArray("LIST_OBJECTS_R") << queryId;
479
480 if (engine) {
481 QQmlContext *rootContext = engine->rootContext();
482 QList<QPointer<QObject>> instances;
483 std::function<void(const QQmlRefPointer<QQmlContextData> &)> collectInstances;
484 collectInstances = [&](const QQmlRefPointer<QQmlContextData> &ctx) {
485 QQmlContextPrivate *ctxPriv =
486 QQmlContextPrivate::get(ctx->asQQmlContext());
487 ctxPriv->cleanInstances(); // Clean deleted objects
488 instances.append(ctxPriv->instances());
489 for (auto child = ctx->childContexts(); child; child = child->nextChild())
490 collectInstances(child);
491 };
492 collectInstances(QQmlContextData::get(rootContext));
493 buildObjectList(rs, rootContext, instances);
494 buildStatesList(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(objectId);
505
506 rs << QByteArray("FETCH_OBJECT_R") << queryId;
507
508 if (object) {
509 if (recurse)
510 prepareDeferredObjects(object);
511 buildObjectDump(rs, object, 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(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(object);
531 buildObjectDump(rs, object, recurse, dumpProperties);
532 }
533
534 } else if (type == "WATCH_OBJECT") {
535 qint32 objectId;
536
537 ds >> objectId;
538 bool ok = m_watch->addWatch(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(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(queryId, 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(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(objectId);
575 QQmlContext *context = qmlContext(object);
576 if (!context || !context->isValid()) {
577 QQmlEngine *engine = qobject_cast<QQmlEngine *>(
578 QQmlDebugService::objectForId(engineId));
579 if (engine && m_engines.contains(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(&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, 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, methodName, methodBody);
625
626 rs << QByteArray("SET_METHOD_BODY_R") << queryId << ok;
627
628 }
629 emit messageToClient(name(), 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(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, line, column, &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(property)->signalIndex(),
659 QQmlContextData::get(context), object, expression.toString(),
660 filename, line, column);
661 QQmlPropertyPrivate::takeSignalExpression(property, qmlExpression);
662 } else if (property.isProperty()) {
663 QQmlBinding *binding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, expression.toString(), object, QQmlContextData::get(context), filename, 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(objectId);
687 QQmlContext *context = qmlContext(object);
688
689 if (object && context && context->isValid()) {
690 QStringView parentPropertyRef(propertyName);
691 const int idx = parentPropertyRef.indexOf(QLatin1Char('.'));
692 if (idx != -1)
693 parentPropertyRef = parentPropertyRef.left(idx);
694
695 const QByteArray parentProperty = parentPropertyRef.toLatin1();
696 if (object->property(parentProperty).isValid()) {
697 QQmlProperty property(object, propertyName);
698 QQmlPropertyPrivate::removeBinding(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(parentProperty).isValid()) {
711 QVariant defaultValue = QQmlProperty(emptyObject, propertyName).read();
712 if (defaultValue.isValid()) {
713 setBinding(objectId, propertyName, defaultValue, 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(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(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(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(QLatin1Char(','));
760 paramStr.append(QString::fromUtf8(paramNames.at(ii)));
761 }
762
763 const QString jsfunction = QLatin1String("(function ") + method + QLatin1Char('(') + paramStr +
764 QLatin1String(") {") + body + QLatin1String("\n})");
765
766 QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(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::Scoped<QV4::JavaScriptFunctionObject> oldMethod(
774 scope, vmeMetaObject->vmeMethod(prop->coreIndex()));
775 if (oldMethod && oldMethod->d()->function)
776 lineNumber = oldMethod->d()->function->compiledFunction->location.line();
777
778 QV4::ScopedValue v(scope, QQmlJavaScriptExpression::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber));
779 vmeMetaObject->setVmeMethod(prop->coreIndex(), v);
780 return true;
781}
782
783void QQmlEngineDebugServiceImpl::propertyChanged(
784 qint32 id, qint32 objectId, const QMetaProperty &property, const QVariant &value)
785{
786 QQmlDebugPacket rs;
787 rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value);
788 emit messageToClient(name(), rs.data());
789}
790
792{
793 Q_ASSERT(engine);
794 Q_ASSERT(!m_engines.contains(engine));
795
796 m_engines.append(engine);
797 emit attachedToEngine(engine);
798}
799
801{
802 Q_ASSERT(engine);
803 Q_ASSERT(m_engines.contains(engine));
804
805 m_engines.removeAll(engine);
806 emit detachedFromEngine(engine);
807}
808
809void QQmlEngineDebugServiceImpl::objectCreated(QJSEngine *engine, QObject *object)
810{
811 Q_ASSERT(engine);
812 if (!m_engines.contains(engine))
813 return;
814
815 qint32 engineId = QQmlDebugService::idForObject(engine);
816 qint32 objectId = QQmlDebugService::idForObject(object);
817 qint32 parentId = QQmlDebugService::idForObject(object->parent());
818
819 QQmlDebugPacket rs;
820
821 //unique queryId -1
822 rs << QByteArray("OBJECT_CREATED") << qint32(-1) << engineId << objectId << parentId;
823 emit messageToClient(name(), rs.data());
824}
825
826QT_END_NAMESPACE
827
828#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 &)