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 QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(rootContext);
483 ctxtPriv->cleanInstances();
484 const QList<QPointer<QObject>> instances = ctxtPriv->instances();
485 buildObjectList(rs, rootContext, instances);
486 buildStatesList(
true, instances);
489 }
else if (type ==
"FETCH_OBJECT") {
492 bool dumpProperties =
true;
494 ds >> objectId >> recurse >> dumpProperties;
496 QObject *object = QQmlDebugService::objectForId(objectId);
498 rs << QByteArray(
"FETCH_OBJECT_R") << queryId;
502 prepareDeferredObjects(object);
503 buildObjectDump(rs, object, recurse, dumpProperties);
506 }
else if (type ==
"FETCH_OBJECTS_FOR_LOCATION") {
511 bool dumpProperties =
true;
513 ds >> file >> lineNumber >> columnNumber >> recurse >> dumpProperties;
515 const QList<QObject*> objects = objectForLocationInfo(file, lineNumber, columnNumber);
517 rs << QByteArray(
"FETCH_OBJECTS_FOR_LOCATION_R") << queryId
518 <<
int(objects.size());
520 for (QObject *object : objects) {
522 prepareDeferredObjects(object);
523 buildObjectDump(rs, object, recurse, dumpProperties);
526 }
else if (type ==
"WATCH_OBJECT") {
530 bool ok = m_watch->addWatch(queryId, objectId);
532 rs << QByteArray(
"WATCH_OBJECT_R") << queryId << ok;
534 }
else if (type ==
"WATCH_PROPERTY") {
538 ds >> objectId >> property;
539 bool ok = m_watch->addWatch(queryId, objectId, property);
541 rs << QByteArray(
"WATCH_PROPERTY_R") << queryId << ok;
543 }
else if (type ==
"WATCH_EXPR_OBJECT") {
547 ds >> debugId >> expr;
548 bool ok = m_watch->addWatch(queryId, debugId, expr);
550 rs << QByteArray(
"WATCH_EXPR_OBJECT_R") << queryId << ok;
552 }
else if (type ==
"NO_WATCH") {
555 rs << QByteArray(
"NO_WATCH_R") << queryId << ok;
557 }
else if (type ==
"EVAL_EXPRESSION") {
561 ds >> objectId >> expr;
562 qint32 engineId = -1;
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();
575 if (context && context->isValid()) {
576 QQmlExpression exprObj(context, object, expr);
577 bool undefined =
false;
578 QVariant value = exprObj.evaluate(&undefined);
580 result = QString(QStringLiteral(
"<undefined>"));
582 result = valueContents(value);
584 result = QString(QStringLiteral(
"<unknown context>"));
587 rs << QByteArray(
"EVAL_EXPRESSION_R") << queryId << result;
589 }
else if (type ==
"SET_BINDING") {
591 QString propertyName;
596 ds >> objectId >> propertyName >> expr >> isLiteralValue >>
598 bool ok = setBinding(objectId, propertyName, expr, isLiteralValue,
601 rs << QByteArray(
"SET_BINDING_R") << queryId << ok;
603 }
else if (type ==
"RESET_BINDING") {
605 QString propertyName;
606 ds >> objectId >> propertyName;
607 bool ok = resetBinding(objectId, propertyName);
609 rs << QByteArray(
"RESET_BINDING_R") << queryId << ok;
611 }
else if (type ==
"SET_METHOD_BODY") {
615 ds >> objectId >> methodName >> methodBody;
616 bool ok = setMethodBody(objectId, methodName, methodBody);
618 rs << QByteArray(
"SET_METHOD_BODY_R") << queryId << ok;
621 emit messageToClient(name(), rs.data());
625 const QString &propertyName,
626 const QVariant &expression,
633 QObject *object = objectForId(objectId);
634 QQmlContext *context = qmlContext(object);
636 if (object && context && context->isValid()) {
638 if (property.isValid()) {
640 bool inBaseState =
true;
642 delegate->updateBinding(context, property, expression, isLiteralValue,
643 filename, line, column, &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);
661 qWarning() <<
"QQmlEngineDebugService::setBinding: unable to set property" << propertyName <<
"on object" << object;
668 ok = delegate->setBindingForInvalidProperty(object, propertyName, expression, isLiteralValue);
670 qWarning() <<
"QQmlEngineDebugService::setBinding: unable to set property" << propertyName <<
"on object" << object;
678 QObject *object = objectForId(objectId);
679 QQmlContext *context = qmlContext(object);
681 if (object && context && context->isValid()) {
682 QStringView parentPropertyRef(propertyName);
683 const int idx = parentPropertyRef.indexOf(QLatin1Char(
'.'));
685 parentPropertyRef = parentPropertyRef.left(idx);
687 const QByteArray parentProperty = parentPropertyRef.toLatin1();
688 if (object->property(parentProperty).isValid()) {
690 QQmlPropertyPrivate::removeBinding(property);
691 if (property.isResettable()) {
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);
715 if (hasValidSignal(object, propertyName)) {
717 QQmlPropertyPrivate::setSignalExpression(property,
nullptr);
722 delegate->resetBindingForInvalidProperty(object, propertyName);
734 QObject *object = objectForId(objectId);
735 QQmlContext *context = qmlContext(object);
736 if (!object || !context || !context->isValid())
738 QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context);
740 QQmlPropertyData dummy;
741 const QQmlPropertyData *prop = QQmlPropertyCache::property(object, method, contextData, &dummy);
743 if (!prop || !prop->isVMEFunction())
746 QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex());
747 QList<QByteArray> paramNames = metaMethod.parameterNames();
750 for (
int ii = 0; ii < paramNames.size(); ++ii) {
751 if (ii != 0) paramStr.append(QLatin1Char(
','));
752 paramStr.append(QString::fromUtf8(paramNames.at(ii)));
755 const QString jsfunction = QLatin1String(
"(function ") + method + QLatin1Char(
'(') + paramStr +
756 QLatin1String(
") {") + body + QLatin1String(
"\n})");
758 QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(object);
759 Q_ASSERT(vmeMetaObject);
761 QV4::ExecutionEngine *v4 = qmlEngine(object)->handle();
762 QV4::Scope scope(v4);
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();
770 QV4::ScopedValue v(scope, QQmlJavaScriptExpression::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber));
771 vmeMetaObject->setVmeMethod(prop->coreIndex(), v);
776 qint32 id, qint32 objectId,
const QMetaProperty &property,
const QVariant &value)
779 rs << QByteArray(
"UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value);
780 emit messageToClient(name(), rs.data());
786 Q_ASSERT(!m_engines.contains(engine));
788 m_engines.append(engine);
789 emit attachedToEngine(engine);
795 Q_ASSERT(m_engines.contains(engine));
797 m_engines.removeAll(engine);
798 emit detachedFromEngine(engine);
804 if (!m_engines.contains(engine))
807 qint32 engineId = QQmlDebugService::idForObject(engine);
808 qint32 objectId = QQmlDebugService::idForObject(object);
809 qint32 parentId = QQmlDebugService::idForObject(object->parent());
814 rs << QByteArray(
"OBJECT_CREATED") << qint32(-1) << engineId << objectId << parentId;
815 emit messageToClient(name(), rs.data());
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
~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 &)