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>
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>
29#include <private/qmetaobject_p.h>
30#include <private/qqmldebugconnector_p.h>
31#include <private/qversionedpacket_p.h>
35using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
63 const int valType =
static_cast<
int>(value.userType());
64 if (valType >= QMetaType::User)
67 QDataStream fakeStream(&nullDevice);
68 return QMetaType(valType).save(fakeStream, value.constData());
72 QQmlEngineDebugService(2, parent), m_watch(
new QQmlWatcher(
this)), m_statesDelegate(
nullptr)
74 connect(m_watch, &QQmlWatcher::propertyChanged,
75 this, &QQmlEngineDebugServiceImpl::propertyChanged);
78 connect(
this, &QQmlEngineDebugServiceImpl::scheduleMessage,
79 this, &QQmlEngineDebugServiceImpl::processMessage, Qt::QueuedConnection);
84 delete m_statesDelegate;
87QDataStream &operator<<(QDataStream &ds,
105QDataStream &operator<<(QDataStream &ds,
108 ds << (
int)data
.type << data.name;
109 ds << (isSaveable(data.value) ? data.value : QVariant());
118 ds >> type >> data.name >> data.value >> data.valueTypeName
126 auto signalName = QQmlSignalNames::handlerNameToSignalName(propertyName);
130 int sigIdx = QQmlPropertyPrivate::findSignalByName(object->metaObject(), signalName->toLatin1())
144 QMetaProperty prop = obj->metaObject()->property(propIdx);
147 rv.valueTypeName = QString::fromUtf8(prop.typeName());
148 rv.name = QString::fromUtf8(prop.name());
150 QQmlAbstractBinding *binding =
151 QQmlPropertyPrivate::binding(QQmlProperty(obj, rv.name));
153 rv.binding = binding->expression();
155 rv.value = valueContents(prop.read(obj));
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()) {
174 if (value.userType() == qMetaTypeId<QJSValue>())
175 value = value.value<QJSValue>().toVariant();
176 const QMetaType metaType = value.metaType();
177 const int metaTypeId = metaType.id();
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));
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()));
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:
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();
219 if (QQmlMetaType::isValueType(metaType)) {
220 const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(metaType);
222 int toStringIndex = mo->indexOfMethod(
"toString()");
223 if (toStringIndex != -1) {
224 QMetaMethod mm = mo->method(toStringIndex);
226 if (mm.invokeOnGadget(value.data(), Q_RETURN_ARG(QString, s)))
232 if (isSaveable(value))
236 if (metaType.flags().testFlag(QMetaType::PointerToQObject)) {
237 QObject *o = QQmlMetaType::toQObject(value);
239 QString name = o->objectName();
241 name = QStringLiteral(
"<unnamed object>");
246 return QString(QStringLiteral(
"<unknown value>"));
250 QObject *object,
bool recur,
bool dumpProperties)
252 message << objectData(object);
254 QObjectList children = object->children();
256 int childrenCount = children.size();
257 for (
int ii = 0; ii < children.size(); ++ii) {
258 if (qobject_cast<QQmlContext*>(children[ii]))
262 message << childrenCount << recur;
264 QList<QQmlObjectProperty> fakeProperties;
266 for (
int ii = 0; ii < children.size(); ++ii) {
267 QObject *child = children.at(ii);
268 if (qobject_cast<QQmlContext*>(child))
271 buildObjectDump(message, child, recur, dumpProperties);
273 message << objectData(child);
276 if (!dumpProperties) {
281 QList<
int> propertyIndexes;
282 for (
int ii = 0; ii < object->metaObject()->propertyCount(); ++ii) {
283 if (object->metaObject()->property(ii).isScriptable())
284 propertyIndexes << ii;
287 QQmlData *ddata = QQmlData::get(object);
288 if (ddata && ddata->signalHandlers) {
289 QQmlBoundSignal *signalHandler = ddata->signalHandlers;
291 while (signalHandler) {
295 QQmlBoundSignalExpression *expr = signalHandler->expression();
297 prop.value = expr->expression();
298 QObject *scope = expr->scopeObject();
300 const QByteArray methodName = QMetaObjectPrivate::signal(scope->metaObject(),
301 signalHandler->signalIndex()).name();
302 if (!methodName.isEmpty()) {
303 prop.name = QQmlSignalNames::signalNameToHandlerName(methodName);
307 fakeProperties << prop;
309 signalHandler = nextSignal(signalHandler);
313 message <<
int(propertyIndexes.size() + fakeProperties.size());
315 for (
int ii = 0; ii < propertyIndexes.size(); ++ii)
316 message << propertyData(object, propertyIndexes.at(ii));
318 for (
int ii = 0; ii < fakeProperties.size(); ++ii)
319 message << fakeProperties[ii];
324 qmlExecuteDeferred(obj);
326 QObjectList children = obj->children();
327 for (
int ii = 0; ii < children.size(); ++ii) {
328 QObject *child = children.at(ii);
329 prepareDeferredObjects(child);
336 QQmlDebugService::idForObject(co);
337 QObjectList children = co->children();
338 for (
int ii = 0; ii < children.size(); ++ii)
339 storeObjectIds(children.at(ii));
344 const QList<QPointer<QObject> > &instances)
346 if (!ctxt->isValid())
349 QQmlRefPointer<QQmlContextData> p = QQmlContextData::get(ctxt);
351 QString ctxtName = ctxt->objectName();
352 int ctxtId = QQmlDebugService::idForObject(ctxt);
353 if (ctxt->contextObject())
354 storeObjectIds(ctxt->contextObject());
356 message << ctxtName << ctxtId;
360 QQmlRefPointer<QQmlContextData> child = p->childContexts();
363 child = child->nextChild();
368 child = p->childContexts();
370 buildObjectList(message, child->asQQmlContext(), instances);
371 child = child->nextChild();
375 for (
int ii = 0; ii < instances.size(); ++ii) {
376 QQmlData *data = QQmlData::get(instances.at(ii));
377 if (data->context == p.data())
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));
390 const QList<QPointer<QObject> > &instances)
393 delegate->buildStatesList(cleanList, instances);
399 QQmlData *ddata = QQmlData::get(object);
401 if (ddata && ddata->outerContext) {
402 rv.url = ddata->outerContext->url();
410 QQmlContext *context = qmlContext(object);
411 if (context && context->isValid())
412 rv.idString = QQmlContextData::get(context)->findObjectId(object);
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);
424 emit scheduleMessage(message);
428
429
431 int lineNumber,
int columnNumber)
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();
450 QQmlDebugPacket ds(message);
454 ds >> type >> queryId;
458 if (type ==
"LIST_ENGINES") {
459 rs << QByteArray(
"LIST_ENGINES_R");
460 rs << queryId <<
int(m_engines.size());
462 for (
int ii = 0; ii < m_engines.size(); ++ii) {
463 QJSEngine *engine = m_engines.at(ii);
465 QString engineName = engine->objectName();
466 qint32 engineId = QQmlDebugService::idForObject(engine);
468 rs << engineName << engineId;
471 }
else if (type ==
"LIST_OBJECTS") {
472 qint32 engineId = -1;
476 qobject_cast<QQmlEngine *>(QQmlDebugService::objectForId(engineId));
478 rs << QByteArray(
"LIST_OBJECTS_R") << queryId;
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();
488 instances.append(ctxPriv->instances());
489 for (
auto child = ctx->childContexts(); child; child = child->nextChild())
490 collectInstances(child);
492 collectInstances(QQmlContextData::get(rootContext));
493 buildObjectList(rs, rootContext, instances);
494 buildStatesList(
true, instances);
497 }
else if (type ==
"FETCH_OBJECT") {
500 bool dumpProperties =
true;
502 ds >> objectId >> recurse >> dumpProperties;
504 QObject *object = QQmlDebugService::objectForId(objectId);
506 rs << QByteArray(
"FETCH_OBJECT_R") << queryId;
510 prepareDeferredObjects(object);
511 buildObjectDump(rs, object, recurse, dumpProperties);
514 }
else if (type ==
"FETCH_OBJECTS_FOR_LOCATION") {
519 bool dumpProperties =
true;
521 ds >> file >> lineNumber >> columnNumber >> recurse >> dumpProperties;
523 const QList<QObject*> objects = objectForLocationInfo(file, lineNumber, columnNumber);
525 rs << QByteArray(
"FETCH_OBJECTS_FOR_LOCATION_R") << queryId
526 <<
int(objects.size());
528 for (QObject *object : objects) {
530 prepareDeferredObjects(object);
531 buildObjectDump(rs, object, recurse, dumpProperties);
534 }
else if (type ==
"WATCH_OBJECT") {
538 bool ok = m_watch->addWatch(queryId, objectId);
540 rs << QByteArray(
"WATCH_OBJECT_R") << queryId << ok;
542 }
else if (type ==
"WATCH_PROPERTY") {
546 ds >> objectId >> property;
547 bool ok = m_watch->addWatch(queryId, objectId, property);
549 rs << QByteArray(
"WATCH_PROPERTY_R") << queryId << ok;
551 }
else if (type ==
"WATCH_EXPR_OBJECT") {
555 ds >> debugId >> expr;
556 bool ok = m_watch->addWatch(queryId, debugId, expr);
558 rs << QByteArray(
"WATCH_EXPR_OBJECT_R") << queryId << ok;
560 }
else if (type ==
"NO_WATCH") {
563 rs << QByteArray(
"NO_WATCH_R") << queryId << ok;
565 }
else if (type ==
"EVAL_EXPRESSION") {
569 ds >> objectId >> expr;
570 qint32 engineId = -1;
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();
583 if (context && context->isValid()) {
584 QQmlExpression exprObj(context, object, expr);
585 bool undefined =
false;
586 QVariant value = exprObj.evaluate(&undefined);
588 result = QString(QStringLiteral(
"<undefined>"));
590 result = valueContents(value);
592 result = QString(QStringLiteral(
"<unknown context>"));
595 rs << QByteArray(
"EVAL_EXPRESSION_R") << queryId << result;
597 }
else if (type ==
"SET_BINDING") {
599 QString propertyName;
604 ds >> objectId >> propertyName >> expr >> isLiteralValue >>
606 bool ok = setBinding(objectId, propertyName, expr, isLiteralValue,
609 rs << QByteArray(
"SET_BINDING_R") << queryId << ok;
611 }
else if (type ==
"RESET_BINDING") {
613 QString propertyName;
614 ds >> objectId >> propertyName;
615 bool ok = resetBinding(objectId, propertyName);
617 rs << QByteArray(
"RESET_BINDING_R") << queryId << ok;
619 }
else if (type ==
"SET_METHOD_BODY") {
623 ds >> objectId >> methodName >> methodBody;
624 bool ok = setMethodBody(objectId, methodName, methodBody);
626 rs << QByteArray(
"SET_METHOD_BODY_R") << queryId << ok;
629 emit messageToClient(name(), rs.data());
633 const QString &propertyName,
634 const QVariant &expression,
641 QObject *object = objectForId(objectId);
642 QQmlContext *context = qmlContext(object);
644 if (object && context && context->isValid()) {
646 if (property.isValid()) {
648 bool inBaseState =
true;
650 delegate->updateBinding(context, property, expression, isLiteralValue,
651 filename, line, column, &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);
669 qWarning() <<
"QQmlEngineDebugService::setBinding: unable to set property" << propertyName <<
"on object" << object;
676 ok = delegate->setBindingForInvalidProperty(object, propertyName, expression, isLiteralValue);
678 qWarning() <<
"QQmlEngineDebugService::setBinding: unable to set property" << propertyName <<
"on object" << object;
686 QObject *object = objectForId(objectId);
687 QQmlContext *context = qmlContext(object);
689 if (object && context && context->isValid()) {
690 QStringView parentPropertyRef(propertyName);
691 const int idx = parentPropertyRef.indexOf(QLatin1Char(
'.'));
693 parentPropertyRef = parentPropertyRef.left(idx);
695 const QByteArray parentProperty = parentPropertyRef.toLatin1();
696 if (object->property(parentProperty).isValid()) {
698 QQmlPropertyPrivate::removeBinding(property);
699 if (property.isResettable()) {
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);
723 if (hasValidSignal(object, propertyName)) {
725 QQmlPropertyPrivate::setSignalExpression(property,
nullptr);
730 delegate->resetBindingForInvalidProperty(object, propertyName);
742 QObject *object = objectForId(objectId);
743 QQmlContext *context = qmlContext(object);
744 if (!object || !context || !context->isValid())
746 QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context);
748 QQmlPropertyData dummy;
749 const QQmlPropertyData *prop = QQmlPropertyCache::property(object, method, contextData, &dummy);
751 if (!prop || !prop->isVMEFunction())
754 QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex());
755 QList<QByteArray> paramNames = metaMethod.parameterNames();
758 for (
int ii = 0; ii < paramNames.size(); ++ii) {
759 if (ii != 0) paramStr.append(QLatin1Char(
','));
760 paramStr.append(QString::fromUtf8(paramNames.at(ii)));
763 const QString jsfunction = QLatin1String(
"(function ") + method + QLatin1Char(
'(') + paramStr +
764 QLatin1String(
") {") + body + QLatin1String(
"\n})");
766 QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(object);
767 Q_ASSERT(vmeMetaObject);
769 QV4::ExecutionEngine *v4 = qmlEngine(object)->handle();
770 QV4::Scope scope(v4);
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();
778 QV4::ScopedValue v(scope, QQmlJavaScriptExpression::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber));
779 vmeMetaObject->setVmeMethod(prop->coreIndex(), v);
784 qint32 id, qint32 objectId,
const QMetaProperty &property,
const QVariant &value)
787 rs << QByteArray(
"UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value);
788 emit messageToClient(name(), rs.data());
794 Q_ASSERT(!m_engines.contains(engine));
796 m_engines.append(engine);
797 emit attachedToEngine(engine);
803 Q_ASSERT(m_engines.contains(engine));
805 m_engines.removeAll(engine);
806 emit detachedFromEngine(engine);
812 if (!m_engines.contains(engine))
815 qint32 engineId = QQmlDebugService::idForObject(engine);
816 qint32 objectId = QQmlDebugService::idForObject(object);
817 qint32 parentId = QQmlDebugService::idForObject(object->parent());
822 rs << QByteArray(
"OBJECT_CREATED") << qint32(-1) << engineId << objectId << parentId;
823 emit messageToClient(name(), rs.data());
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
~QQmlEngineDebugServiceImpl()
void engineAboutToBeRemoved(QJSEngine *) override
QQmlDebugStatesDelegate * statesDelegate()
The QQmlProperty class abstracts accessing properties on objects created from QML.
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 &)