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 QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(rootContext);
483 ctxtPriv->cleanInstances(); // Clean deleted objects
484 const QList<QPointer<QObject>> instances = ctxtPriv->instances();
485 buildObjectList(rs, rootContext, instances);
486 buildStatesList(true, instances);
487 }
488
489 } else if (type == "FETCH_OBJECT") {
490 qint32 objectId;
491 bool recurse;
492 bool dumpProperties = true;
493
494 ds >> objectId >> recurse >> dumpProperties;
495
496 QObject *object = QQmlDebugService::objectForId(objectId);
497
498 rs << QByteArray("FETCH_OBJECT_R") << queryId;
499
500 if (object) {
501 if (recurse)
502 prepareDeferredObjects(object);
503 buildObjectDump(rs, object, recurse, dumpProperties);
504 }
505
506 } else if (type == "FETCH_OBJECTS_FOR_LOCATION") {
507 QString file;
508 qint32 lineNumber;
509 qint32 columnNumber;
510 bool recurse;
511 bool dumpProperties = true;
512
513 ds >> file >> lineNumber >> columnNumber >> recurse >> dumpProperties;
514
515 const QList<QObject*> objects = objectForLocationInfo(file, lineNumber, columnNumber);
516
517 rs << QByteArray("FETCH_OBJECTS_FOR_LOCATION_R") << queryId
518 << int(objects.size());
519
520 for (QObject *object : objects) {
521 if (recurse)
522 prepareDeferredObjects(object);
523 buildObjectDump(rs, object, recurse, dumpProperties);
524 }
525
526 } else if (type == "WATCH_OBJECT") {
527 qint32 objectId;
528
529 ds >> objectId;
530 bool ok = m_watch->addWatch(queryId, objectId);
531
532 rs << QByteArray("WATCH_OBJECT_R") << queryId << ok;
533
534 } else if (type == "WATCH_PROPERTY") {
535 qint32 objectId;
536 QByteArray property;
537
538 ds >> objectId >> property;
539 bool ok = m_watch->addWatch(queryId, objectId, property);
540
541 rs << QByteArray("WATCH_PROPERTY_R") << queryId << ok;
542
543 } else if (type == "WATCH_EXPR_OBJECT") {
544 qint32 debugId;
545 QString expr;
546
547 ds >> debugId >> expr;
548 bool ok = m_watch->addWatch(queryId, debugId, expr);
549
550 rs << QByteArray("WATCH_EXPR_OBJECT_R") << queryId << ok;
551
552 } else if (type == "NO_WATCH") {
553 bool ok = m_watch->removeWatch(queryId);
554
555 rs << QByteArray("NO_WATCH_R") << queryId << ok;
556
557 } else if (type == "EVAL_EXPRESSION") {
558 qint32 objectId;
559 QString expr;
560
561 ds >> objectId >> expr;
562 qint32 engineId = -1;
563 if (!ds.atEnd())
564 ds >> engineId;
565
566 QObject *object = QQmlDebugService::objectForId(objectId);
567 QQmlContext *context = qmlContext(object);
568 if (!context || !context->isValid()) {
569 QQmlEngine *engine = qobject_cast<QQmlEngine *>(
570 QQmlDebugService::objectForId(engineId));
571 if (engine && m_engines.contains(engine))
572 context = engine->rootContext();
573 }
574 QVariant result;
575 if (context && context->isValid()) {
576 QQmlExpression exprObj(context, object, expr);
577 bool undefined = false;
578 QVariant value = exprObj.evaluate(&undefined);
579 if (undefined)
580 result = QString(QStringLiteral("<undefined>"));
581 else
582 result = valueContents(value);
583 } else {
584 result = QString(QStringLiteral("<unknown context>"));
585 }
586
587 rs << QByteArray("EVAL_EXPRESSION_R") << queryId << result;
588
589 } else if (type == "SET_BINDING") {
590 qint32 objectId;
591 QString propertyName;
592 QVariant expr;
593 bool isLiteralValue;
594 QString filename;
595 qint32 line;
596 ds >> objectId >> propertyName >> expr >> isLiteralValue >>
597 filename >> line;
598 bool ok = setBinding(objectId, propertyName, expr, isLiteralValue,
599 filename, line);
600
601 rs << QByteArray("SET_BINDING_R") << queryId << ok;
602
603 } else if (type == "RESET_BINDING") {
604 qint32 objectId;
605 QString propertyName;
606 ds >> objectId >> propertyName;
607 bool ok = resetBinding(objectId, propertyName);
608
609 rs << QByteArray("RESET_BINDING_R") << queryId << ok;
610
611 } else if (type == "SET_METHOD_BODY") {
612 qint32 objectId;
613 QString methodName;
614 QString methodBody;
615 ds >> objectId >> methodName >> methodBody;
616 bool ok = setMethodBody(objectId, methodName, methodBody);
617
618 rs << QByteArray("SET_METHOD_BODY_R") << queryId << ok;
619
620 }
621 emit messageToClient(name(), rs.data());
622}
623
624bool QQmlEngineDebugServiceImpl::setBinding(int objectId,
625 const QString &propertyName,
626 const QVariant &expression,
627 bool isLiteralValue,
628 QString filename,
629 int line,
630 int column)
631{
632 bool ok = true;
633 QObject *object = objectForId(objectId);
634 QQmlContext *context = qmlContext(object);
635
636 if (object && context && context->isValid()) {
637 QQmlProperty property(object, propertyName, context);
638 if (property.isValid()) {
639
640 bool inBaseState = true;
641 if (auto delegate = statesDelegate()) {
642 delegate->updateBinding(context, property, expression, isLiteralValue,
643 filename, line, column, &inBaseState);
644 }
645
646 if (inBaseState) {
647 if (isLiteralValue) {
648 property.write(expression);
649 } else if (hasValidSignal(object, propertyName)) {
650 QQmlBoundSignalExpression *qmlExpression = new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(property)->signalIndex(),
651 QQmlContextData::get(context), object, expression.toString(),
652 filename, line, column);
653 QQmlPropertyPrivate::takeSignalExpression(property, qmlExpression);
654 } else if (property.isProperty()) {
655 QQmlBinding *binding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, expression.toString(), object, QQmlContextData::get(context), filename, line);
656 binding->setTarget(property);
657 QQmlPropertyPrivate::setBinding(binding);
658 binding->update();
659 } else {
660 ok = false;
661 qWarning() << "QQmlEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object;
662 }
663 }
664
665 } else {
666 // not a valid property
667 if (auto delegate = statesDelegate())
668 ok = delegate->setBindingForInvalidProperty(object, propertyName, expression, isLiteralValue);
669 if (!ok)
670 qWarning() << "QQmlEngineDebugService::setBinding: unable to set property" << propertyName << "on object" << object;
671 }
672 }
673 return ok;
674}
675
676bool QQmlEngineDebugServiceImpl::resetBinding(int objectId, const QString &propertyName)
677{
678 QObject *object = objectForId(objectId);
679 QQmlContext *context = qmlContext(object);
680
681 if (object && context && context->isValid()) {
682 QStringView parentPropertyRef(propertyName);
683 const int idx = parentPropertyRef.indexOf(QLatin1Char('.'));
684 if (idx != -1)
685 parentPropertyRef = parentPropertyRef.left(idx);
686
687 const QByteArray parentProperty = parentPropertyRef.toLatin1();
688 if (object->property(parentProperty).isValid()) {
689 QQmlProperty property(object, propertyName);
690 QQmlPropertyPrivate::removeBinding(property);
691 if (property.isResettable()) {
692 // Note: this will reset the property in any case, without regard to states
693 // Right now almost no QQuickItem has reset methods for its properties (with the
694 // notable exception of QQuickAnchors), so this is not a big issue
695 // later on, setBinding does take states into account
696 property.reset();
697 } else {
698 // overwrite with default value
699 QQmlType objType = QQmlMetaType::qmlType(object->metaObject());
700 if (objType.isValid()) {
701 if (QObject *emptyObject = objType.create()) {
702 if (emptyObject->property(parentProperty).isValid()) {
703 QVariant defaultValue = QQmlProperty(emptyObject, propertyName).read();
704 if (defaultValue.isValid()) {
705 setBinding(objectId, propertyName, defaultValue, true);
706 }
707 }
708 delete emptyObject;
709 }
710 }
711 }
712 return true;
713 }
714
715 if (hasValidSignal(object, propertyName)) {
716 QQmlProperty property(object, propertyName, context);
717 QQmlPropertyPrivate::setSignalExpression(property, nullptr);
718 return true;
719 }
720
721 if (auto delegate = statesDelegate()) {
722 delegate->resetBindingForInvalidProperty(object, propertyName);
723 return true;
724 }
725
726 return false;
727 }
728 // object or context null.
729 return false;
730}
731
732bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &method, const QString &body)
733{
734 QObject *object = objectForId(objectId);
735 QQmlContext *context = qmlContext(object);
736 if (!object || !context || !context->isValid())
737 return false;
738 QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context);
739
740 QQmlPropertyData dummy;
741 const QQmlPropertyData *prop = QQmlPropertyCache::property(object, method, contextData, &dummy);
742
743 if (!prop || !prop->isVMEFunction())
744 return false;
745
746 QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex());
747 QList<QByteArray> paramNames = metaMethod.parameterNames();
748
749 QString paramStr;
750 for (int ii = 0; ii < paramNames.size(); ++ii) {
751 if (ii != 0) paramStr.append(QLatin1Char(','));
752 paramStr.append(QString::fromUtf8(paramNames.at(ii)));
753 }
754
755 const QString jsfunction = QLatin1String("(function ") + method + QLatin1Char('(') + paramStr +
756 QLatin1String(") {") + body + QLatin1String("\n})");
757
758 QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(object);
759 Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this
760
761 QV4::ExecutionEngine *v4 = qmlEngine(object)->handle();
762 QV4::Scope scope(v4);
763
764 int lineNumber = 0;
765 QV4::Scoped<QV4::JavaScriptFunctionObject> oldMethod(
766 scope, vmeMetaObject->vmeMethod(prop->coreIndex()));
767 if (oldMethod && oldMethod->d()->function)
768 lineNumber = oldMethod->d()->function->compiledFunction->location.line();
769
770 QV4::ScopedValue v(scope, QQmlJavaScriptExpression::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber));
771 vmeMetaObject->setVmeMethod(prop->coreIndex(), v);
772 return true;
773}
774
775void QQmlEngineDebugServiceImpl::propertyChanged(
776 qint32 id, qint32 objectId, const QMetaProperty &property, const QVariant &value)
777{
778 QQmlDebugPacket rs;
779 rs << QByteArray("UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value);
780 emit messageToClient(name(), rs.data());
781}
782
784{
785 Q_ASSERT(engine);
786 Q_ASSERT(!m_engines.contains(engine));
787
788 m_engines.append(engine);
789 emit attachedToEngine(engine);
790}
791
793{
794 Q_ASSERT(engine);
795 Q_ASSERT(m_engines.contains(engine));
796
797 m_engines.removeAll(engine);
798 emit detachedFromEngine(engine);
799}
800
801void QQmlEngineDebugServiceImpl::objectCreated(QJSEngine *engine, QObject *object)
802{
803 Q_ASSERT(engine);
804 if (!m_engines.contains(engine))
805 return;
806
807 qint32 engineId = QQmlDebugService::idForObject(engine);
808 qint32 objectId = QQmlDebugService::idForObject(object);
809 qint32 parentId = QQmlDebugService::idForObject(object->parent());
810
811 QQmlDebugPacket rs;
812
813 //unique queryId -1
814 rs << QByteArray("OBJECT_CREATED") << qint32(-1) << engineId << objectId << parentId;
815 emit messageToClient(name(), rs.data());
816}
817
818QT_END_NAMESPACE
819
820#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 &)