7#include <private/qqmldebugstatesdelegate_p.h>
8#include <private/qqmlboundsignal_p.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#include <private/qqmlsignalnames_p.h>
20#include <QtCore/qdebug.h>
21#include <QtCore/qmetaobject.h>
22#include <QtCore/qfileinfo.h>
23#include <QtCore/qjsonvalue.h>
24#include <QtCore/qjsonobject.h>
25#include <QtCore/qjsonarray.h>
26#include <QtCore/qjsondocument.h>
28#include <private/qmetaobject_p.h>
29#include <private/qqmldebugconnector_p.h>
30#include <private/qversionedpacket_p.h>
34using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
62 const int valType =
static_cast<
int>(value.userType());
63 if (valType >= QMetaType::User)
66 QDataStream fakeStream(&nullDevice);
67 return QMetaType(valType).save(fakeStream, value.constData());
71 QQmlEngineDebugService(2, parent), m_watch(
new QQmlWatcher(
this)), m_statesDelegate(
nullptr)
73 connect(m_watch, &QQmlWatcher::propertyChanged,
74 this, &QQmlEngineDebugServiceImpl::propertyChanged);
77 connect(
this, &QQmlEngineDebugServiceImpl::scheduleMessage,
78 this, &QQmlEngineDebugServiceImpl::processMessage, Qt::QueuedConnection);
83 delete m_statesDelegate;
86QDataStream &operator<<(QDataStream &ds,
104QDataStream &operator<<(QDataStream &ds,
107 ds << (
int)data
.type << data.name;
108 ds << (isSaveable(data.value) ? data.value : QVariant());
117 ds >> type >> data.name >> data.value >> data.valueTypeName
125 auto signalName = QQmlSignalNames::handlerNameToSignalName(propertyName);
129 int sigIdx = QQmlPropertyPrivate::findSignalByName(object->metaObject(), signalName->toLatin1())
143 QMetaProperty prop = obj->metaObject()->property(propIdx);
146 rv.valueTypeName = QString::fromUtf8(prop.typeName());
147 rv.name = QString::fromUtf8(prop.name());
149 QQmlAbstractBinding *binding =
150 QQmlPropertyPrivate::binding(QQmlProperty(obj, rv.name));
152 rv.binding = binding->expression();
154 rv.value = valueContents(prop.read(obj));
156 if (prop.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
158 }
else if (QQmlMetaType::isList(prop.metaType())) {
160 }
else if (prop.userType() == QMetaType::QVariant) {
162 }
else if (rv.value.isValid()) {
173 if (value.userType() == qMetaTypeId<QJSValue>())
174 value = value.value<QJSValue>().toVariant();
175 const QMetaType metaType = value.metaType();
176 const int metaTypeId = metaType.id();
181 if (value.userType() == QMetaType::QVariantList) {
182 QVariantList contents;
183 QVariantList list = value.toList();
184 int count = list.size();
185 contents.reserve(count);
186 for (
int i = 0; i < count; i++)
187 contents << valueContents(list.at(i));
191 if (value.userType() == QMetaType::QVariantMap) {
192 QVariantMap contents;
193 const auto map = value.toMap();
194 for (
auto i = map.cbegin(), end = map.cend(); i != end; ++i)
195 contents.insert(i.key(), valueContents(i.value()));
199 switch (metaTypeId) {
200 case QMetaType::QRect:
201 case QMetaType::QRectF:
202 case QMetaType::QPoint:
203 case QMetaType::QPointF:
204 case QMetaType::QSize:
205 case QMetaType::QSizeF:
206 case QMetaType::QFont:
209 case QMetaType::QJsonValue:
210 return value.toJsonValue().toVariant();
211 case QMetaType::QJsonObject:
212 return value.toJsonObject().toVariantMap();
213 case QMetaType::QJsonArray:
214 return value.toJsonArray().toVariantList();
215 case QMetaType::QJsonDocument:
216 return value.toJsonDocument().toVariant();
218 if (QQmlMetaType::isValueType(metaType)) {
219 const QMetaObject *mo = QQmlMetaType::metaObjectForValueType(metaType);
221 int toStringIndex = mo->indexOfMethod(
"toString()");
222 if (toStringIndex != -1) {
223 QMetaMethod mm = mo->method(toStringIndex);
225 if (mm.invokeOnGadget(value.data(), Q_RETURN_ARG(QString, s)))
231 if (isSaveable(value))
235 if (metaType.flags().testFlag(QMetaType::PointerToQObject)) {
236 QObject *o = QQmlMetaType::toQObject(value);
238 QString name = o->objectName();
240 name = QStringLiteral(
"<unnamed object>");
245 return QString(QStringLiteral(
"<unknown value>"));
249 QObject *object,
bool recur,
bool dumpProperties)
251 message << objectData(object);
253 QObjectList children = object->children();
255 int childrenCount = children.size();
256 for (
int ii = 0; ii < children.size(); ++ii) {
257 if (qobject_cast<QQmlContext*>(children[ii]))
261 message << childrenCount << recur;
263 QList<QQmlObjectProperty> fakeProperties;
265 for (
int ii = 0; ii < children.size(); ++ii) {
266 QObject *child = children.at(ii);
267 if (qobject_cast<QQmlContext*>(child))
270 buildObjectDump(message, child, recur, dumpProperties);
272 message << objectData(child);
275 if (!dumpProperties) {
280 QList<
int> propertyIndexes;
281 for (
int ii = 0; ii < object->metaObject()->propertyCount(); ++ii) {
282 if (object->metaObject()->property(ii).isScriptable())
283 propertyIndexes << ii;
286 QQmlData *ddata = QQmlData::get(object);
287 if (ddata && ddata->signalHandlers) {
288 QQmlBoundSignal *signalHandler = ddata->signalHandlers;
290 while (signalHandler) {
294 QQmlBoundSignalExpression *expr = signalHandler->expression();
296 prop.value = expr->expression();
297 QObject *scope = expr->scopeObject();
299 const QByteArray methodName = QMetaObjectPrivate::signal(scope->metaObject(),
300 signalHandler->signalIndex()).name();
301 if (!methodName.isEmpty()) {
302 prop.name = QQmlSignalNames::signalNameToHandlerName(methodName);
306 fakeProperties << prop;
308 signalHandler = nextSignal(signalHandler);
312 message <<
int(propertyIndexes.size() + fakeProperties.size());
314 for (
int ii = 0; ii < propertyIndexes.size(); ++ii)
315 message << propertyData(object, propertyIndexes.at(ii));
317 for (
int ii = 0; ii < fakeProperties.size(); ++ii)
318 message << fakeProperties[ii];
323 qmlExecuteDeferred(obj);
325 QObjectList children = obj->children();
326 for (
int ii = 0; ii < children.size(); ++ii) {
327 QObject *child = children.at(ii);
328 prepareDeferredObjects(child);
335 QQmlDebugService::idForObject(co);
336 QObjectList children = co->children();
337 for (
int ii = 0; ii < children.size(); ++ii)
338 storeObjectIds(children.at(ii));
343 const QList<QPointer<QObject> > &instances)
345 if (!ctxt->isValid())
348 QQmlRefPointer<QQmlContextData> p = QQmlContextData::get(ctxt);
350 QString ctxtName = ctxt->objectName();
351 int ctxtId = QQmlDebugService::idForObject(ctxt);
352 if (ctxt->contextObject())
353 storeObjectIds(ctxt->contextObject());
355 message << ctxtName << ctxtId;
359 QQmlRefPointer<QQmlContextData> child = p->childContexts();
362 child = child->nextChild();
367 child = p->childContexts();
369 buildObjectList(message, child->asQQmlContext(), instances);
370 child = child->nextChild();
374 for (
int ii = 0; ii < instances.size(); ++ii) {
375 QQmlData *data = QQmlData::get(instances.at(ii));
376 if (data->context == p.data())
381 for (
int ii = 0; ii < instances.size(); ++ii) {
382 QQmlData *data = QQmlData::get(instances.at(ii));
383 if (data->context == p.data())
384 message << objectData(instances.at(ii));
389 const QList<QPointer<QObject> > &instances)
392 delegate->buildStatesList(cleanList, instances);
398 QQmlData *ddata = QQmlData::get(object);
400 if (ddata && ddata->outerContext) {
401 rv.url = ddata->outerContext->url();
409 QQmlContext *context = qmlContext(object);
410 if (context && context->isValid())
411 rv.idString = QQmlContextData::get(context)->findObjectId(object);
413 rv.objectName = object->objectName();
414 rv.objectId = QQmlDebugService::idForObject(object);
415 rv.contextId = QQmlDebugService::idForObject(qmlContext(object));
416 rv.parentId = QQmlDebugService::idForObject(object->parent());
417 rv.objectType = QQmlMetaType::prettyTypeName(object);
423 emit scheduleMessage(message);
427
428
430 int lineNumber,
int columnNumber)
432 QList<QObject *> objects;
433 const QHash<
int, QObject *> &hash = objectsForIds();
434 for (QHash<
int, QObject *>::ConstIterator i = hash.constBegin(); i != hash.constEnd(); ++i) {
435 QQmlData *ddata = QQmlData::get(i.value());
436 if (ddata && ddata->outerContext && ddata->outerContext->isValid()) {
437 if (QFileInfo(ddata->outerContext->urlString()).fileName() == filename &&
438 ddata->lineNumber == lineNumber &&
439 ddata->columnNumber >= columnNumber) {
440 objects << i.value();
449 QQmlDebugPacket ds(message);
453 ds >> type >> queryId;
457 if (type ==
"LIST_ENGINES") {
458 rs << QByteArray(
"LIST_ENGINES_R");
459 rs << queryId <<
int(m_engines.size());
461 for (
int ii = 0; ii < m_engines.size(); ++ii) {
462 QJSEngine *engine = m_engines.at(ii);
464 QString engineName = engine->objectName();
465 qint32 engineId = QQmlDebugService::idForObject(engine);
467 rs << engineName << engineId;
470 }
else if (type ==
"LIST_OBJECTS") {
471 qint32 engineId = -1;
475 qobject_cast<QQmlEngine *>(QQmlDebugService::objectForId(engineId));
477 rs << QByteArray(
"LIST_OBJECTS_R") << queryId;
480 QQmlContext *rootContext = engine->rootContext();
481 QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(rootContext);
482 ctxtPriv->cleanInstances();
483 const QList<QPointer<QObject>> instances = ctxtPriv->instances();
484 buildObjectList(rs, rootContext, instances);
485 buildStatesList(
true, instances);
488 }
else if (type ==
"FETCH_OBJECT") {
491 bool dumpProperties =
true;
493 ds >> objectId >> recurse >> dumpProperties;
495 QObject *object = QQmlDebugService::objectForId(objectId);
497 rs << QByteArray(
"FETCH_OBJECT_R") << queryId;
501 prepareDeferredObjects(object);
502 buildObjectDump(rs, object, recurse, dumpProperties);
505 }
else if (type ==
"FETCH_OBJECTS_FOR_LOCATION") {
510 bool dumpProperties =
true;
512 ds >> file >> lineNumber >> columnNumber >> recurse >> dumpProperties;
514 const QList<QObject*> objects = objectForLocationInfo(file, lineNumber, columnNumber);
516 rs << QByteArray(
"FETCH_OBJECTS_FOR_LOCATION_R") << queryId
517 <<
int(objects.size());
519 for (QObject *object : objects) {
521 prepareDeferredObjects(object);
522 buildObjectDump(rs, object, recurse, dumpProperties);
525 }
else if (type ==
"WATCH_OBJECT") {
529 bool ok = m_watch->addWatch(queryId, objectId);
531 rs << QByteArray(
"WATCH_OBJECT_R") << queryId << ok;
533 }
else if (type ==
"WATCH_PROPERTY") {
537 ds >> objectId >> property;
538 bool ok = m_watch->addWatch(queryId, objectId, property);
540 rs << QByteArray(
"WATCH_PROPERTY_R") << queryId << ok;
542 }
else if (type ==
"WATCH_EXPR_OBJECT") {
546 ds >> debugId >> expr;
547 bool ok = m_watch->addWatch(queryId, debugId, expr);
549 rs << QByteArray(
"WATCH_EXPR_OBJECT_R") << queryId << ok;
551 }
else if (type ==
"NO_WATCH") {
554 rs << QByteArray(
"NO_WATCH_R") << queryId << ok;
556 }
else if (type ==
"EVAL_EXPRESSION") {
560 ds >> objectId >> expr;
561 qint32 engineId = -1;
565 QObject *object = QQmlDebugService::objectForId(objectId);
566 QQmlContext *context = qmlContext(object);
567 if (!context || !context->isValid()) {
568 QQmlEngine *engine = qobject_cast<QQmlEngine *>(
569 QQmlDebugService::objectForId(engineId));
570 if (engine && m_engines.contains(engine))
571 context = engine->rootContext();
574 if (context && context->isValid()) {
575 QQmlExpression exprObj(context, object, expr);
576 bool undefined =
false;
577 QVariant value = exprObj.evaluate(&undefined);
579 result = QString(QStringLiteral(
"<undefined>"));
581 result = valueContents(value);
583 result = QString(QStringLiteral(
"<unknown context>"));
586 rs << QByteArray(
"EVAL_EXPRESSION_R") << queryId << result;
588 }
else if (type ==
"SET_BINDING") {
590 QString propertyName;
595 ds >> objectId >> propertyName >> expr >> isLiteralValue >>
597 bool ok = setBinding(objectId, propertyName, expr, isLiteralValue,
600 rs << QByteArray(
"SET_BINDING_R") << queryId << ok;
602 }
else if (type ==
"RESET_BINDING") {
604 QString propertyName;
605 ds >> objectId >> propertyName;
606 bool ok = resetBinding(objectId, propertyName);
608 rs << QByteArray(
"RESET_BINDING_R") << queryId << ok;
610 }
else if (type ==
"SET_METHOD_BODY") {
614 ds >> objectId >> methodName >> methodBody;
615 bool ok = setMethodBody(objectId, methodName, methodBody);
617 rs << QByteArray(
"SET_METHOD_BODY_R") << queryId << ok;
620 emit messageToClient(name(), rs.data());
624 const QString &propertyName,
625 const QVariant &expression,
632 QObject *object = objectForId(objectId);
633 QQmlContext *context = qmlContext(object);
635 if (object && context && context->isValid()) {
637 if (property.isValid()) {
639 bool inBaseState =
true;
641 delegate->updateBinding(context, property, expression, isLiteralValue,
642 filename, line, column, &inBaseState);
646 if (isLiteralValue) {
647 property.write(expression);
648 }
else if (hasValidSignal(object, propertyName)) {
649 QQmlBoundSignalExpression *qmlExpression =
new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(property)->signalIndex(),
650 QQmlContextData::get(context), object, expression.toString(),
651 filename, line, column);
652 QQmlPropertyPrivate::takeSignalExpression(property, qmlExpression);
653 }
else if (property.isProperty()) {
654 QQmlBinding *binding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, expression.toString(), object, QQmlContextData::get(context), filename, line);
655 binding->setTarget(property);
656 QQmlPropertyPrivate::setBinding(binding);
660 qWarning() <<
"QQmlEngineDebugService::setBinding: unable to set property" << propertyName <<
"on object" << object;
667 ok = delegate->setBindingForInvalidProperty(object, propertyName, expression, isLiteralValue);
669 qWarning() <<
"QQmlEngineDebugService::setBinding: unable to set property" << propertyName <<
"on object" << object;
677 QObject *object = objectForId(objectId);
678 QQmlContext *context = qmlContext(object);
680 if (object && context && context->isValid()) {
681 QStringView parentPropertyRef(propertyName);
682 const int idx = parentPropertyRef.indexOf(QLatin1Char(
'.'));
684 parentPropertyRef = parentPropertyRef.left(idx);
686 const QByteArray parentProperty = parentPropertyRef.toLatin1();
687 if (object->property(parentProperty).isValid()) {
689 QQmlPropertyPrivate::removeBinding(property);
690 if (property.isResettable()) {
698 QQmlType objType = QQmlMetaType::qmlType(object->metaObject());
699 if (objType.isValid()) {
700 if (QObject *emptyObject = objType.create()) {
701 if (emptyObject->property(parentProperty).isValid()) {
702 QVariant defaultValue = QQmlProperty(emptyObject, propertyName).read();
703 if (defaultValue.isValid()) {
704 setBinding(objectId, propertyName, defaultValue,
true);
714 if (hasValidSignal(object, propertyName)) {
716 QQmlPropertyPrivate::setSignalExpression(property,
nullptr);
721 delegate->resetBindingForInvalidProperty(object, propertyName);
733 QObject *object = objectForId(objectId);
734 QQmlContext *context = qmlContext(object);
735 if (!object || !context || !context->isValid())
737 QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context);
739 QQmlPropertyData dummy;
740 const QQmlPropertyData *prop = QQmlPropertyCache::property(object, method, contextData, &dummy);
742 if (!prop || !prop->isVMEFunction())
745 QMetaMethod metaMethod = object->metaObject()->method(prop->coreIndex());
746 QList<QByteArray> paramNames = metaMethod.parameterNames();
749 for (
int ii = 0; ii < paramNames.size(); ++ii) {
750 if (ii != 0) paramStr.append(QLatin1Char(
','));
751 paramStr.append(QString::fromUtf8(paramNames.at(ii)));
754 const QString jsfunction = QLatin1String(
"(function ") + method + QLatin1Char(
'(') + paramStr +
755 QLatin1String(
") {") + body + QLatin1String(
"\n})");
757 QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(object);
758 Q_ASSERT(vmeMetaObject);
760 QV4::ExecutionEngine *v4 = qmlEngine(object)->handle();
761 QV4::Scope scope(v4);
764 QV4::Scoped<QV4::JavaScriptFunctionObject> oldMethod(
765 scope, vmeMetaObject->vmeMethod(prop->coreIndex()));
766 if (oldMethod && oldMethod->d()->function)
767 lineNumber = oldMethod->d()->function->compiledFunction->location.line();
769 QV4::ScopedValue v(scope, QQmlJavaScriptExpression::evalFunction(contextData, object, jsfunction, contextData->urlString(), lineNumber));
770 vmeMetaObject->setVmeMethod(prop->coreIndex(), v);
775 qint32 id, qint32 objectId,
const QMetaProperty &property,
const QVariant &value)
778 rs << QByteArray(
"UPDATE_WATCH") << id << objectId << QByteArray(property.name()) << valueContents(value);
779 emit messageToClient(name(), rs.data());
785 Q_ASSERT(!m_engines.contains(engine));
787 m_engines.append(engine);
788 emit attachedToEngine(engine);
794 Q_ASSERT(m_engines.contains(engine));
796 m_engines.removeAll(engine);
797 emit detachedFromEngine(engine);
803 if (!m_engines.contains(engine))
806 qint32 engineId = QQmlDebugService::idForObject(engine);
807 qint32 objectId = QQmlDebugService::idForObject(object);
808 qint32 parentId = QQmlDebugService::idForObject(object->parent());
813 rs << QByteArray(
"OBJECT_CREATED") << qint32(-1) << engineId << objectId << parentId;
814 emit messageToClient(name(), rs.data());
819#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 &)