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
qdbusmetaobject.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
7
8#include <QtCore/qbytearray.h>
9#include <QtCore/qhash.h>
10#include <QtCore/qstring.h>
11#include <QtCore/qvarlengtharray.h>
12
13#include "qdbusutil_p.h"
14#include "qdbuserror.h"
15#include "qdbusmetatype.h"
16#include "qdbusargument.h"
19
20#include <private/qmetaobject_p.h>
21#include <private/qmetaobjectbuilder_p.h>
22
23#ifndef QT_NO_DBUS
24
26
27using namespace Qt::StringLiterals;
28
30{
31public:
32 QDBusMetaObjectGenerator(const QString &interface,
33 const QDBusIntrospection::Interface *parsedData);
34 void write(QDBusMetaObject *obj);
35 void writeWithoutXml(QDBusMetaObject *obj);
36
37private:
38 struct Method {
39 QList<QByteArray> parameterNames;
40 QByteArray tag;
41 QByteArray name;
42 QVarLengthArray<int, 4> inputTypes;
43 QVarLengthArray<int, 4> outputTypes;
44 QByteArray rawReturnType;
45 quint32 flags;
46 };
47
48 struct Property {
49 QByteArray typeName;
50 QByteArray signature;
51 int type;
52 quint32 flags;
53 };
54 struct Type {
55 int id;
56 QByteArray name;
57 };
58
60 MethodMap signals_;
61 MethodMap methods;
62 QMap<QByteArray, Property> properties;
63
65 QString interface;
66
67 Type findType(const QByteArray &signature,
68 const QDBusIntrospection::Annotations &annotations,
69 const char *direction = "Out", int id = -1);
70
71 void parseMethods();
72 void parseSignals();
73 void parseProperties();
74
75 static qsizetype aggregateParameterCount(const MethodMap &map);
76};
77
78static const qsizetype intsPerProperty = 2;
79static const qsizetype intsPerMethod = 2;
80
86
88 const QDBusIntrospection::Interface *parsedData)
90{
91 if (data) {
92 parseProperties();
93 parseSignals(); // call parseSignals first so that slots override signals
94 parseMethods();
95 }
96}
97
98static int registerComplexDBusType(const QByteArray &typeName)
99{
100 struct QDBusRawTypeHandler : QtPrivate::QMetaTypeInterface
101 {
102 const QByteArray name;
103 QDBusRawTypeHandler(const QByteArray &name)
104 : QtPrivate::QMetaTypeInterface {
105 0, sizeof(void *), sizeof(void *), QMetaType::RelocatableType, 0, nullptr,
106 name.constData(),
107 nullptr, nullptr, nullptr, nullptr,
108 nullptr, nullptr, nullptr,
109 nullptr, nullptr, nullptr
110 },
111 name(name)
112 {}
113 };
114
115 Q_CONSTINIT static QBasicMutex mutex;
116 Q_CONSTINIT static struct Hash : QHash<QByteArray, QMetaType>
117 {
118 ~Hash()
119 {
120 for (QMetaType entry : std::as_const(*this))
121 QMetaType::unregisterMetaType(entry);
122 }
123 } hash;
124 QMutexLocker lock(&mutex);
125 QMetaType &metatype = hash[typeName];
126 if (!metatype.isValid())
127 metatype = QMetaType(new QDBusRawTypeHandler(typeName));
128 return metatype.id();
129}
130
131Q_DBUS_EXPORT bool qt_dbus_metaobject_skip_annotations = false;
132
134QDBusMetaObjectGenerator::findType(const QByteArray &signature,
135 const QDBusIntrospection::Annotations &annotations,
136 const char *direction, int id)
137{
138 Type result;
139 result.id = QMetaType::UnknownType;
140
141 int type = QDBusMetaType::signatureToMetaType(signature).id();
142 if (type == QMetaType::UnknownType && !qt_dbus_metaobject_skip_annotations) {
143 // it's not a type normally handled by our meta type system
144 // it must contain an annotation
145 QString annotationName = QString::fromLatin1("org.qtproject.QtDBus.QtTypeName");
146 if (id >= 0)
147 annotationName += QString::fromLatin1(".%1%2")
148 .arg(QLatin1StringView(direction))
149 .arg(id);
150
151 // extract from annotations:
152 auto annotation = annotations.value(annotationName);
153 QByteArray typeName = annotation.value.toLatin1();
154
155 // verify that it's a valid one
156 if (typeName.isEmpty()) {
157 // try the old annotation from Qt 4
158 annotationName = QString::fromLatin1("com.trolltech.QtDBus.QtTypeName");
159 if (id >= 0)
160 annotationName += QString::fromLatin1(".%1%2")
161 .arg(QLatin1StringView(direction))
162 .arg(id);
163 annotation = annotations.value(annotationName);
164 typeName = annotation.value.toLatin1();
165 }
166
167 if (!typeName.isEmpty()) {
168 // type name found
169 type = QMetaType::fromName(typeName).id();
170 }
171
172 if (type == QMetaType::UnknownType || signature != QDBusMetaType::typeToSignature(QMetaType(type))) {
173 // type is still unknown or doesn't match back to the signature that it
174 // was expected to, so synthesize a fake type
175 typeName = "QDBusRawType<0x" + signature.toHex() + ">*";
176 Q_ASSERT(typeName == QMetaObject::normalizedType(typeName));
177 type = registerComplexDBusType(typeName);
178 }
179
180 result.name = typeName;
181 } else if (type == QMetaType::UnknownType) {
182 // this case is used only by the qdbus command-line tool
183 // invalid, let's create an impossible type that contains the signature
184
185 if (signature == "av") {
186 result.name = "QVariantList";
187 type = QMetaType::QVariantList;
188 } else if (signature == "a{sv}") {
189 result.name = "QVariantMap";
190 type = QMetaType::QVariantMap;
191 } else if (signature == "a{ss}") {
192 result.name = "QMap<QString,QString>";
193 type = qMetaTypeId<QMap<QString, QString> >();
194 } else if (signature == "aay") {
195 result.name = "QByteArrayList";
196 type = qMetaTypeId<QByteArrayList>();
197 } else {
198 result.name = "{D-Bus type \"" + signature + "\"}";
199 result.name = QMetaObject::normalizedType(result.name);
200 type = registerComplexDBusType(result.name);
201 }
202 } else {
203 result.name = QMetaType(type).name();
204 }
205
206 result.id = type;
207 return result; // success
208}
209
210void QDBusMetaObjectGenerator::parseMethods()
211{
212 //
213 // TODO:
214 // Add cloned methods when the remote object has return types
215 //
216
217 for (const QDBusIntrospection::Method &m : std::as_const(data->methods)) {
218 Method mm;
219
220 mm.name = m.name.toLatin1();
221 QByteArray prototype = mm.name;
222 prototype += '(';
223
224 bool ok = true;
225
226 // build the input argument list
227 for (qsizetype i = 0; i < m.inputArgs.size(); ++i) {
228 const QDBusIntrospection::Argument &arg = m.inputArgs.at(i);
229
230 Type type = findType(arg.type.toLatin1(), m.annotations, "In", i);
231 if (type.id == QMetaType::UnknownType) {
232 ok = false;
233 break;
234 }
235
236 mm.inputTypes.append(type.id);
237
238 mm.parameterNames.append(arg.name.toLatin1());
239
240 prototype.append(type.name);
241 prototype.append(',');
242 }
243 if (!ok) continue;
244
245 // build the output argument list:
246 for (qsizetype i = 0; i < m.outputArgs.size(); ++i) {
247 const QDBusIntrospection::Argument &arg = m.outputArgs.at(i);
248
249 Type type = findType(arg.type.toLatin1(), m.annotations, "Out", i);
250 if (type.id == QMetaType::UnknownType) {
251 ok = false;
252 break;
253 }
254
255 mm.outputTypes.append(type.id);
256
257 if (i == 0 && type.id == -1) {
258 mm.rawReturnType = type.name;
259 }
260 if (i != 0) {
261 // non-const ref parameter
262 mm.parameterNames.append(arg.name.toLatin1());
263
264 prototype.append(type.name);
265 prototype.append("&,");
266 }
267 }
268 if (!ok) continue;
269
270 // convert the last commas:
271 if (!mm.parameterNames.isEmpty())
272 prototype[prototype.size() - 1] = ')';
273 else
274 prototype.append(')');
275
276 // check the async tag
277 if (m.annotations.value(ANNOTATION_NO_WAIT ""_L1).value == "true"_L1)
278 mm.tag = "Q_NOREPLY";
279
280 // meta method flags
281 mm.flags = AccessPublic | MethodSlot | MethodScriptable;
282
283 // add
284 methods.insert(QMetaObject::normalizedSignature(prototype), mm);
285 }
286}
287
288void QDBusMetaObjectGenerator::parseSignals()
289{
290 for (const QDBusIntrospection::Signal &s : std::as_const(data->signals_)) {
291 Method mm;
292
293 mm.name = s.name.toLatin1();
294 QByteArray prototype = mm.name;
295 prototype += '(';
296
297 bool ok = true;
298
299 // build the output argument list
300 for (qsizetype i = 0; i < s.outputArgs.size(); ++i) {
301 const QDBusIntrospection::Argument &arg = s.outputArgs.at(i);
302
303 Type type = findType(arg.type.toLatin1(), s.annotations, "Out", i);
304 if (type.id == QMetaType::UnknownType) {
305 ok = false;
306 break;
307 }
308
309 mm.inputTypes.append(type.id);
310
311 mm.parameterNames.append(arg.name.toLatin1());
312
313 prototype.append(type.name);
314 prototype.append(',');
315 }
316 if (!ok) continue;
317
318 // convert the last commas:
319 if (!mm.parameterNames.isEmpty())
320 prototype[prototype.size() - 1] = ')';
321 else
322 prototype.append(')');
323
324 // meta method flags
325 mm.flags = AccessPublic | MethodSignal | MethodScriptable;
326
327 // add
328 signals_.insert(QMetaObject::normalizedSignature(prototype), mm);
329 }
330}
331
332void QDBusMetaObjectGenerator::parseProperties()
333{
334 for (const QDBusIntrospection::Property &p : std::as_const(data->properties)) {
335 Property mp;
336 Type type = findType(p.type.toLatin1(), p.annotations);
337 if (type.id == QMetaType::UnknownType)
338 continue;
339
340 QByteArray name = p.name.toLatin1();
341 mp.signature = p.type.toLatin1();
342 mp.type = type.id;
343 mp.typeName = type.name;
344
345 // build the flags:
346 mp.flags = StdCppSet | Scriptable | Stored | Designable;
347 if (p.access != QDBusIntrospection::Property::Write)
348 mp.flags |= Readable;
349 if (p.access != QDBusIntrospection::Property::Read)
350 mp.flags |= Writable;
351
352 // add the property:
353 properties.insert(name, mp);
354 }
355}
356
357// Returns the sum of all parameters (including return type) for the given
358// \a map of methods. This is needed for calculating the size of the methods'
359// parameter type/name meta-data.
360qsizetype QDBusMetaObjectGenerator::aggregateParameterCount(const MethodMap &map)
361{
362 qsizetype sum = 0;
363 for (const Method &m : map)
364 sum += m.inputTypes.size() + qMax(qsizetype(1), m.outputTypes.size());
365 return sum;
366}
367
368void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj)
369{
370 // this code here is mostly copied from qaxbase.cpp
371 // with a few modifications to make it cleaner
372
373 QString className = interface;
374 className.replace(u'.', "::"_L1);
375 if (className.isEmpty())
376 className = "QDBusInterface"_L1;
377
378 QVarLengthArray<uint> idata;
379 idata.resize(sizeof(QDBusMetaObjectPrivate) / sizeof(uint));
380
381 qsizetype methodParametersDataSize =
382 ((aggregateParameterCount(signals_)
383 + aggregateParameterCount(methods)) * 2) // types and parameter names
384 - signals_.size() // return "parameters" don't have names
385 - methods.size(); // ditto
386
387 QDBusMetaObjectPrivate *header = reinterpret_cast<QDBusMetaObjectPrivate *>(idata.data());
388 static_assert(QMetaObjectPrivate::OutputRevision == 13, "QtDBus meta-object generator should generate the same version as moc");
389 header->revision = QMetaObjectPrivate::OutputRevision;
390 header->className = 0;
391 header->classInfoCount = 0;
392 header->classInfoData = 0;
393 header->methodCount = int(signals_.size() + methods.size());
394 header->methodData = int(idata.size());
395 header->propertyCount = int(properties.size());
396 header->propertyData = int(header->methodData + header->methodCount *
397 QMetaObjectPrivate::IntsPerMethod + methodParametersDataSize);
398 header->enumeratorCount = 0;
399 header->enumeratorData = 0;
400 header->constructorCount = 0;
401 header->constructorData = 0;
402 header->flags = RequiresVariantMetaObject | AllocatedMetaObject;
403 header->signalCount = signals_.size();
404 // These are specific to QDBusMetaObject:
405 header->propertyDBusData = int(header->propertyData + header->propertyCount
406 * QMetaObjectPrivate::IntsPerProperty);
407 header->methodDBusData = int(header->propertyDBusData + header->propertyCount * intsPerProperty);
408
409 qsizetype data_size = idata.size() +
410 (header->methodCount * (QMetaObjectPrivate::IntsPerMethod+intsPerMethod)) + methodParametersDataSize +
411 (header->propertyCount * (QMetaObjectPrivate::IntsPerProperty+intsPerProperty));
412
413 // Signals must be added before other methods, to match moc.
414 std::array<std::reference_wrapper<const MethodMap>, 2> methodMaps = { signals_, methods };
415
416 for (const auto &methodMap : methodMaps) {
417 for (const Method &mm : methodMap.get())
418 data_size += 2 + mm.inputTypes.size() + mm.outputTypes.size();
419 }
420 idata.resize(data_size + 1);
421
422 QMetaStringTable strings(className.toLatin1());
423
424 qsizetype offset = header->methodData;
425 qsizetype parametersOffset = offset + header->methodCount * QMetaObjectPrivate::IntsPerMethod;
426 qsizetype signatureOffset = header->methodDBusData;
427 qsizetype typeidOffset = header->methodDBusData + header->methodCount * intsPerMethod;
428 idata[typeidOffset++] = 0; // eod
429
430 qsizetype totalMetaTypeCount = properties.size();
431 ++totalMetaTypeCount; // + 1 for metatype of dynamic metaobject
432 for (const auto &methodMap : methodMaps) {
433 for (const Method &mm : methodMap.get()) {
434 qsizetype argc = mm.inputTypes.size() + qMax(qsizetype(0), mm.outputTypes.size() - 1);
435 totalMetaTypeCount += argc + 1;
436 }
437 }
438 QMetaType *metaTypes = new QMetaType[totalMetaTypeCount];
439 int propertyId = 0;
440
441 // add each method:
442 qsizetype currentMethodMetaTypeOffset = properties.size() + 1;
443
444 for (const auto &methodMap : methodMaps) {
445 for (const Method &mm : methodMap.get()) {
446 qsizetype argc = mm.inputTypes.size() + qMax(qsizetype(0), mm.outputTypes.size() - 1);
447
448 idata[offset++] = strings.enter(mm.name);
449 idata[offset++] = argc;
450 idata[offset++] = parametersOffset;
451 idata[offset++] = strings.enter(mm.tag);
452 idata[offset++] = mm.flags;
453 idata[offset++] = currentMethodMetaTypeOffset;
454
455 // Parameter types
456 for (qsizetype i = -1; i < argc; ++i) {
457 int type;
458 QByteArray typeName;
459 if (i < 0) { // Return type
460 if (!mm.outputTypes.isEmpty()) {
461 type = mm.outputTypes.first();
462 if (type == -1) {
463 type = IsUnresolvedType | strings.enter(mm.rawReturnType);
464 }
465 } else {
466 type = QMetaType::Void;
467 }
468 } else if (i < mm.inputTypes.size()) {
469 type = mm.inputTypes.at(i);
470 } else {
471 Q_ASSERT(mm.outputTypes.size() > 1);
472 type = mm.outputTypes.at(i - mm.inputTypes.size() + 1);
473 // Output parameters are references; type id not available
474 typeName = QMetaType(type).name();
475 typeName.append('&');
476 type = QMetaType::UnknownType;
477 }
478 int typeInfo;
479 if (!typeName.isEmpty())
480 typeInfo = IsUnresolvedType | strings.enter(typeName);
481 else
482 typeInfo = type;
483 metaTypes[currentMethodMetaTypeOffset++] = QMetaType(type);
484 idata[parametersOffset++] = typeInfo;
485 }
486 // Parameter names
487 for (qsizetype i = 0; i < argc; ++i)
488 idata[parametersOffset++] = strings.enter(mm.parameterNames.at(i));
489
490 idata[signatureOffset++] = typeidOffset;
491 idata[typeidOffset++] = mm.inputTypes.size();
492 memcpy(idata.data() + typeidOffset, mm.inputTypes.data(), mm.inputTypes.size() * sizeof(uint));
493 typeidOffset += mm.inputTypes.size();
494
495 idata[signatureOffset++] = typeidOffset;
496 idata[typeidOffset++] = mm.outputTypes.size();
497 memcpy(idata.data() + typeidOffset, mm.outputTypes.data(), mm.outputTypes.size() * sizeof(uint));
498 typeidOffset += mm.outputTypes.size();
499 }
500 }
501
502 Q_ASSERT(offset == header->methodData + header->methodCount * QMetaObjectPrivate::IntsPerMethod);
503 Q_ASSERT(parametersOffset == header->propertyData);
504 Q_ASSERT(signatureOffset == header->methodDBusData + header->methodCount * intsPerMethod);
505 Q_ASSERT(typeidOffset == idata.size());
506 offset += methodParametersDataSize;
507 Q_ASSERT(offset == header->propertyData);
508
509 // add each property
510 signatureOffset = header->propertyDBusData;
511 for (const auto &[name, mp] : std::as_const(properties).asKeyValueRange()) {
512 // form is name, typeinfo, flags
513 idata[offset++] = strings.enter(name);
514 Q_ASSERT(mp.type != QMetaType::UnknownType);
515 idata[offset++] = mp.type;
516 idata[offset++] = mp.flags;
517 idata[offset++] = -1; // notify index
518 idata[offset++] = 0; // revision
519
520 idata[signatureOffset++] = strings.enter(mp.signature);
521 idata[signatureOffset++] = mp.type;
522
523 metaTypes[propertyId++] = QMetaType(mp.type);
524 }
525 metaTypes[propertyId] = QMetaType(); // we can't know our own metatype
526
527 Q_ASSERT(offset == header->propertyDBusData);
528 Q_ASSERT(signatureOffset == header->methodDBusData);
529
530 char *string_data = new char[strings.blobSize()];
531 strings.writeBlob(string_data);
532
533 uint *uint_data = new uint[idata.size()];
534 memcpy(uint_data, idata.data(), idata.size() * sizeof(uint));
535
536 // put the metaobject together
537 obj->d.data = uint_data;
538 obj->d.relatedMetaObjects = nullptr;
539 obj->d.static_metacall = nullptr;
540 obj->d.extradata = nullptr;
541 obj->d.stringdata = reinterpret_cast<const uint *>(string_data);
542 obj->d.superdata = &QDBusAbstractInterface::staticMetaObject;
543 obj->d.metaTypes = reinterpret_cast<QtPrivate::QMetaTypeInterface *const *>(metaTypes);
544}
545
546#if 0
547void QDBusMetaObjectGenerator::writeWithoutXml(const QString &interface)
548{
549 // no XML definition
550 QString tmp(interface);
551 tmp.replace(u'.', "::"_L1);
552 QByteArray name(tmp.toLatin1());
553
554 QDBusMetaObjectPrivate *header = new QDBusMetaObjectPrivate;
555 memset(header, 0, sizeof *header);
556 header->revision = 1;
557 // leave the rest with 0
558
559 char *stringdata = new char[name.length() + 1];
560 stringdata[name.length()] = '\0';
561
562 d.data = reinterpret_cast<uint*>(header);
563 d.relatedMetaObjects = 0;
564 d.static_metacall = 0;
565 d.extradata = 0;
566 d.stringdata = stringdata;
567 d.superdata = &QDBusAbstractInterface::staticMetaObject;
568 cached = false;
569}
570#endif
571
572/////////
573// class QDBusMetaObject
574
575QDBusMetaObject *QDBusMetaObject::createMetaObject(const QString &interface, const QString &xml,
576 QHash<QString, QDBusMetaObject *> &cache,
577 QDBusError &error)
578{
579 error = QDBusError();
580 QDBusIntrospection::Interfaces parsed = QDBusIntrospection::parseInterfaces(xml);
581
582 QDBusMetaObject *we = nullptr;
583 QDBusIntrospection::Interfaces::ConstIterator it = parsed.constBegin();
584 QDBusIntrospection::Interfaces::ConstIterator end = parsed.constEnd();
585 for ( ; it != end; ++it) {
586 // check if it's in the cache
587 bool us = it.key() == interface;
588
589 QDBusMetaObject *obj = cache.value(it.key(), 0);
590 if (!obj && (us || !interface.startsWith("local."_L1 ))) {
591 // not in cache; create
592 obj = new QDBusMetaObject;
593 QDBusMetaObjectGenerator generator(it.key(), it.value().constData());
594 generator.write(obj);
595
596 if ((obj->cached = !it.key().startsWith("local."_L1)))
597 // cache it
598 cache.insert(it.key(), obj);
599 else if (!us)
600 delete obj;
601
602 }
603
604 if (us)
605 // it's us
606 we = obj;
607 }
608
609 if (we)
610 return we;
611 // still nothing?
612
613 if (parsed.isEmpty()) {
614 // object didn't return introspection
615 we = new QDBusMetaObject;
616 QDBusMetaObjectGenerator generator(interface, nullptr);
617 generator.write(we);
618 we->cached = false;
619 return we;
620 } else if (interface.isEmpty()) {
621 // merge all interfaces
622 it = parsed.constBegin();
623 QDBusIntrospection::Interface merged = *it.value().constData();
624
625 for (++it; it != end; ++it) {
626 merged.annotations.insert(it.value()->annotations);
627 merged.methods.unite(it.value()->methods);
628 merged.signals_.unite(it.value()->signals_);
629 merged.properties.insert(it.value()->properties);
630 }
631
632 merged.name = "local.Merged"_L1;
633 merged.introspection.clear();
634
635 we = new QDBusMetaObject;
636 QDBusMetaObjectGenerator generator(merged.name, &merged);
637 generator.write(we);
638 we->cached = false;
639 return we;
640 }
641
642 // mark as an error
643 error = QDBusError(QDBusError::UnknownInterface,
644 "Interface '%1' was not found"_L1.arg(interface));
645 return nullptr;
646}
647
648QDBusMetaObject::QDBusMetaObject()
649{
650}
651
652static inline const QDBusMetaObjectPrivate *priv(const uint* data)
653{
654 return reinterpret_cast<const QDBusMetaObjectPrivate *>(data);
655}
656
657const int *QDBusMetaObject::inputTypesForMethod(int id) const
658{
659 //id -= methodOffset();
660 if (id >= 0 && id < priv(d.data)->methodCount) {
661 int handle = priv(d.data)->methodDBusData + id*intsPerMethod;
662 return reinterpret_cast<const int*>(d.data + d.data[handle]);
663 }
664 return nullptr;
665}
666
667const int *QDBusMetaObject::outputTypesForMethod(int id) const
668{
669 //id -= methodOffset();
670 if (id >= 0 && id < priv(d.data)->methodCount) {
671 int handle = priv(d.data)->methodDBusData + id*intsPerMethod;
672 return reinterpret_cast<const int*>(d.data + d.data[handle + 1]);
673 }
674 return nullptr;
675}
676
677int QDBusMetaObject::propertyMetaType(int id) const
678{
679 //id -= propertyOffset();
680 if (id >= 0 && id < priv(d.data)->propertyCount) {
681 int handle = priv(d.data)->propertyDBusData + id*intsPerProperty;
682 return d.data[handle + 1];
683 }
684 return 0;
685}
686
687QT_END_NAMESPACE
688
689#endif // QT_NO_DBUS
void writeWithoutXml(QDBusMetaObject *obj)
QDBusMetaObjectGenerator(const QString &interface, const QDBusIntrospection::Interface *parsedData)
void write(QDBusMetaObject *obj)
Combined button and popup list for selecting options.
#define ANNOTATION_NO_WAIT
Q_DBUS_EXPORT bool qt_dbus_metaobject_skip_annotations
static const qsizetype intsPerMethod
static int registerComplexDBusType(const QByteArray &typeName)
static const qsizetype intsPerProperty