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