1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qdbusmetatype.h" |
5 | #include "qdbusmetatype_p.h" |
6 | |
7 | #include <string.h> |
8 | #include "qdbus_symbols_p.h" |
9 | |
10 | #include <qbytearray.h> |
11 | #include <qglobal.h> |
12 | #include <qlist.h> |
13 | #include <qreadwritelock.h> |
14 | #include <qdatetime.h> |
15 | #include <qrect.h> |
16 | #include <qsize.h> |
17 | #include <qpoint.h> |
18 | #include <qline.h> |
19 | |
20 | #include "qdbusargument_p.h" |
21 | #include "qdbusutil_p.h" |
22 | #include "qdbusunixfiledescriptor.h" |
23 | #ifndef QT_BOOTSTRAPPED |
24 | #include "qdbusmessage.h" |
25 | #endif |
26 | |
27 | #ifndef QT_NO_DBUS |
28 | |
29 | #ifndef DBUS_TYPE_UNIX_FD |
30 | # define DBUS_TYPE_UNIX_FD int('h') |
31 | # define DBUS_TYPE_UNIX_FD_AS_STRING "h" |
32 | #endif |
33 | |
34 | QT_BEGIN_NAMESPACE |
35 | |
36 | class QDBusCustomTypeInfo |
37 | { |
38 | public: |
39 | QDBusCustomTypeInfo() : signature(), marshall(nullptr), demarshall(nullptr) |
40 | { } |
41 | |
42 | // Suggestion: |
43 | // change 'signature' to char* and make QDBusCustomTypeInfo a Movable type |
44 | QByteArray signature; |
45 | QDBusMetaType::MarshallFunction marshall; |
46 | QDBusMetaType::DemarshallFunction demarshall; |
47 | }; |
48 | |
49 | void QDBusMetaTypeId::init() |
50 | { |
51 | Q_CONSTINIT static QBasicAtomicInt initialized = Q_BASIC_ATOMIC_INITIALIZER(false); |
52 | |
53 | // reentrancy is not a problem since everything else is locked on their own |
54 | // set the guard variable at the end |
55 | if (!initialized.loadRelaxed()) { |
56 | // register our types with Qt Core |
57 | message().registerType(); |
58 | argument().registerType(); |
59 | variant().registerType(); |
60 | objectpath().registerType(); |
61 | signature().registerType(); |
62 | error().registerType(); |
63 | unixfd().registerType(); |
64 | |
65 | #ifndef QDBUS_NO_SPECIALTYPES |
66 | // and register Qt Core's with us |
67 | qDBusRegisterMetaType<QDate>(); |
68 | qDBusRegisterMetaType<QTime>(); |
69 | qDBusRegisterMetaType<QDateTime>(); |
70 | qDBusRegisterMetaType<QRect>(); |
71 | qDBusRegisterMetaType<QRectF>(); |
72 | qDBusRegisterMetaType<QSize>(); |
73 | qDBusRegisterMetaType<QSizeF>(); |
74 | qDBusRegisterMetaType<QPoint>(); |
75 | qDBusRegisterMetaType<QPointF>(); |
76 | qDBusRegisterMetaType<QLine>(); |
77 | qDBusRegisterMetaType<QLineF>(); |
78 | qDBusRegisterMetaType<QVariantList>(); |
79 | qDBusRegisterMetaType<QVariantMap>(); |
80 | qDBusRegisterMetaType<QVariantHash>(); |
81 | |
82 | qDBusRegisterMetaType<QList<bool> >(); |
83 | qDBusRegisterMetaType<QList<short> >(); |
84 | qDBusRegisterMetaType<QList<ushort> >(); |
85 | qDBusRegisterMetaType<QList<int> >(); |
86 | qDBusRegisterMetaType<QList<uint> >(); |
87 | qDBusRegisterMetaType<QList<qlonglong> >(); |
88 | qDBusRegisterMetaType<QList<qulonglong> >(); |
89 | qDBusRegisterMetaType<QList<double> >(); |
90 | |
91 | // plus lists of our own types |
92 | qDBusRegisterMetaType<QList<QDBusVariant> >(); |
93 | qDBusRegisterMetaType<QList<QDBusObjectPath> >(); |
94 | qDBusRegisterMetaType<QList<QDBusSignature> >(); |
95 | qDBusRegisterMetaType<QList<QDBusUnixFileDescriptor> >(); |
96 | #endif |
97 | |
98 | initialized.storeRelaxed(newValue: true); |
99 | } |
100 | } |
101 | |
102 | struct QDBusCustomTypes |
103 | { |
104 | QReadWriteLock lock; |
105 | QHash<int, QDBusCustomTypeInfo> hash; |
106 | }; |
107 | |
108 | Q_GLOBAL_STATIC(QDBusCustomTypes, customTypes) |
109 | |
110 | /*! |
111 | \class QDBusMetaType |
112 | \inmodule QtDBus |
113 | \brief Meta-type registration system for the Qt D-Bus module. |
114 | \internal |
115 | |
116 | The QDBusMetaType class allows you to register class types for |
117 | marshalling and demarshalling over D-Bus. D-Bus supports a very |
118 | limited set of primitive types, but allows one to extend the type |
119 | system by creating compound types, such as arrays (lists) and |
120 | structs. In order to use them with Qt D-Bus, those types must be |
121 | registered. |
122 | |
123 | See \l {qdbustypesystem.html}{Qt D-Bus Type System} for more |
124 | information on the type system and how to register additional |
125 | types. |
126 | |
127 | \sa {qdbustypesystem.html}{Qt D-Bus Type System}, |
128 | qDBusRegisterMetaType(), QMetaType, QVariant, QDBusArgument |
129 | */ |
130 | |
131 | /*! |
132 | \fn int qDBusRegisterMetaType() |
133 | \relates QDBusArgument |
134 | \threadsafe |
135 | \since 4.2 |
136 | |
137 | Registers \c{T} with the |
138 | \l {qdbustypesystem.html}{Qt D-Bus Type System} and the Qt \l |
139 | {QMetaType}{meta-type system}, if it's not already registered. |
140 | |
141 | To register a type, it must be declared as a meta-type with the |
142 | Q_DECLARE_METATYPE() macro, and then registered as in the |
143 | following example: |
144 | |
145 | \snippet code/src_qdbus_qdbusmetatype.cpp 0-0 |
146 | \codeline |
147 | \snippet code/src_qdbus_qdbusmetatype.cpp 0-1 |
148 | |
149 | If \c{T} isn't one of |
150 | Qt's \l{container classes}, the \c{operator<<} and |
151 | \c{operator>>} streaming operators between \c{T} and QDBusArgument |
152 | must be already declared. See the \l {qdbustypesystem.html}{Qt D-Bus |
153 | Type System} page for more information on how to declare such |
154 | types. |
155 | |
156 | This function returns the Qt meta type id for the type (the same |
157 | value that is returned from qRegisterMetaType()). |
158 | |
159 | \note The feature that a \c{T} inheriting a streamable type (including |
160 | the containers QList, QHash or QMap) can be streamed without providing |
161 | custom \c{operator<<} and \c{operator>>} is deprecated as of Qt 5.7, |
162 | because it ignores everything in \c{T} except the base class. There is |
163 | no diagnostic. You should always provide these operators for all types |
164 | you wish to stream and not rely on Qt-provided stream operators for |
165 | base classes. |
166 | |
167 | \sa {qdbustypesystem.html}{Qt D-Bus Type System}, qRegisterMetaType(), QMetaType |
168 | */ |
169 | |
170 | /*! |
171 | \typedef QDBusMetaType::MarshallFunction |
172 | \internal |
173 | */ |
174 | |
175 | /*! |
176 | \typedef QDBusMetaType::DemarshallFunction |
177 | \internal |
178 | */ |
179 | |
180 | /*! |
181 | \internal |
182 | Registers the marshalling and demarshalling functions for meta |
183 | type \a metaType. |
184 | */ |
185 | void QDBusMetaType::registerMarshallOperators(QMetaType metaType, MarshallFunction mf, |
186 | DemarshallFunction df) |
187 | { |
188 | int id = metaType.id(); |
189 | if (id < 0 || !mf || !df) |
190 | return; // error! |
191 | |
192 | auto *ct = customTypes(); |
193 | if (!ct) |
194 | return; |
195 | |
196 | QWriteLocker locker(&ct->lock); |
197 | QDBusCustomTypeInfo &info = ct->hash[id]; |
198 | info.marshall = mf; |
199 | info.demarshall = df; |
200 | } |
201 | |
202 | /*! |
203 | \internal |
204 | Executes the marshalling of type \a metaType (whose data is contained in |
205 | \a data) to the D-Bus marshalling argument \a arg. Returns \c true if |
206 | the marshalling succeeded, or false if an error occurred. |
207 | */ |
208 | bool QDBusMetaType::marshall(QDBusArgument &arg, QMetaType metaType, const void *data) |
209 | { |
210 | auto *ct = customTypes(); |
211 | if (!ct) |
212 | return false; |
213 | |
214 | int id = metaType.id(); |
215 | QDBusMetaTypeId::init(); |
216 | |
217 | MarshallFunction mf; |
218 | { |
219 | QReadLocker locker(&ct->lock); |
220 | |
221 | auto it = ct->hash.constFind(key: id); |
222 | if (it == ct->hash.cend()) |
223 | return false; // non-existent |
224 | |
225 | const QDBusCustomTypeInfo &info = *it; |
226 | if (!info.marshall) { |
227 | mf = nullptr; // make gcc happy |
228 | return false; |
229 | } else |
230 | mf = info.marshall; |
231 | } |
232 | |
233 | mf(arg, data); |
234 | return true; |
235 | } |
236 | |
237 | /*! |
238 | \internal |
239 | Executes the demarshalling of type \a metaType (whose data will be placed in |
240 | \a data) from the D-Bus marshalling argument \a arg. Returns \c true if |
241 | the demarshalling succeeded, or false if an error occurred. |
242 | */ |
243 | bool QDBusMetaType::demarshall(const QDBusArgument &arg, QMetaType metaType, void *data) |
244 | { |
245 | auto *ct = customTypes(); |
246 | if (!ct) |
247 | return false; |
248 | |
249 | int id = metaType.id(); |
250 | QDBusMetaTypeId::init(); |
251 | |
252 | DemarshallFunction df; |
253 | { |
254 | QReadLocker locker(&ct->lock); |
255 | |
256 | auto it = ct->hash.constFind(key: id); |
257 | if (it == ct->hash.cend()) |
258 | return false; // non-existent |
259 | |
260 | const QDBusCustomTypeInfo &info = *it; |
261 | if (!info.demarshall) { |
262 | df = nullptr; // make gcc happy |
263 | return false; |
264 | } else |
265 | df = info.demarshall; |
266 | } |
267 | #ifndef QT_BOOTSTRAPPED |
268 | QDBusArgument copy = arg; |
269 | df(copy, data); |
270 | #else |
271 | Q_UNUSED(arg); |
272 | Q_UNUSED(data); |
273 | Q_UNUSED(df); |
274 | #endif |
275 | return true; |
276 | } |
277 | |
278 | /*! |
279 | \fn QDBusMetaType::signatureToType(const char *signature) |
280 | \internal |
281 | |
282 | Returns the Qt meta type id for the given D-Bus signature for exactly one full type, given |
283 | by \a signature. |
284 | |
285 | Note: this function only handles the basic D-Bus types. |
286 | |
287 | \sa QDBusUtil::isValidSingleSignature(), typeToSignature(), |
288 | QVariant::metaType() |
289 | */ |
290 | QMetaType QDBusMetaType::signatureToMetaType(const char *signature) |
291 | { |
292 | if (!signature) |
293 | return QMetaType(QMetaType::UnknownType); |
294 | |
295 | QDBusMetaTypeId::init(); |
296 | switch (signature[0]) |
297 | { |
298 | case DBUS_TYPE_BOOLEAN: |
299 | return QMetaType(QMetaType::Bool); |
300 | |
301 | case DBUS_TYPE_BYTE: |
302 | return QMetaType(QMetaType::UChar); |
303 | |
304 | case DBUS_TYPE_INT16: |
305 | return QMetaType(QMetaType::Short); |
306 | |
307 | case DBUS_TYPE_UINT16: |
308 | return QMetaType(QMetaType::UShort); |
309 | |
310 | case DBUS_TYPE_INT32: |
311 | return QMetaType(QMetaType::Int); |
312 | |
313 | case DBUS_TYPE_UINT32: |
314 | return QMetaType(QMetaType::UInt); |
315 | |
316 | case DBUS_TYPE_INT64: |
317 | return QMetaType(QMetaType::LongLong); |
318 | |
319 | case DBUS_TYPE_UINT64: |
320 | return QMetaType(QMetaType::ULongLong); |
321 | |
322 | case DBUS_TYPE_DOUBLE: |
323 | return QMetaType(QMetaType::Double); |
324 | |
325 | case DBUS_TYPE_STRING: |
326 | return QMetaType(QMetaType::QString); |
327 | |
328 | case DBUS_TYPE_OBJECT_PATH: |
329 | return QDBusMetaTypeId::objectpath(); |
330 | |
331 | case DBUS_TYPE_SIGNATURE: |
332 | return QDBusMetaTypeId::signature(); |
333 | |
334 | case DBUS_TYPE_UNIX_FD: |
335 | return QDBusMetaTypeId::unixfd(); |
336 | |
337 | case DBUS_TYPE_VARIANT: |
338 | return QDBusMetaTypeId::variant(); |
339 | |
340 | case DBUS_TYPE_ARRAY: // special case |
341 | switch (signature[1]) { |
342 | case DBUS_TYPE_BYTE: |
343 | return QMetaType(QMetaType::QByteArray); |
344 | |
345 | case DBUS_TYPE_STRING: |
346 | return QMetaType(QMetaType::QStringList); |
347 | |
348 | case DBUS_TYPE_VARIANT: |
349 | return QMetaType(QMetaType::QVariantList); |
350 | |
351 | case DBUS_TYPE_OBJECT_PATH: |
352 | return QMetaType::fromType<QList<QDBusObjectPath> >(); |
353 | |
354 | case DBUS_TYPE_SIGNATURE: |
355 | return QMetaType::fromType<QList<QDBusSignature> >(); |
356 | |
357 | } |
358 | Q_FALLTHROUGH(); |
359 | default: |
360 | return QMetaType(QMetaType::UnknownType); |
361 | } |
362 | } |
363 | |
364 | /*! |
365 | \fn QDBusMetaType::registerCustomType(QMetaType type, const QByteArray &signature) |
366 | \internal |
367 | |
368 | Registers the meta type \a type to be represeneted by the given D-Bus \a signature. |
369 | |
370 | This is used in qdbuscpp2xml for custom types which aren't known to the C++ type system. |
371 | */ |
372 | void QDBusMetaType::registerCustomType(QMetaType type, const QByteArray &signature) |
373 | { |
374 | auto *ct = customTypes(); |
375 | if (!ct) |
376 | return; |
377 | |
378 | QWriteLocker locker(&ct->lock); |
379 | auto &info = ct->hash[type.id()]; |
380 | info.signature = signature; |
381 | // note how marshall/demarshall are not set, the type is never used at runtime |
382 | } |
383 | |
384 | /*! |
385 | \fn QDBusMetaType::typeToSignature(int type) |
386 | \internal |
387 | |
388 | Returns the D-Bus signature equivalent to the supplied meta type id \a type. |
389 | |
390 | More types can be registered with the qDBusRegisterMetaType() function. |
391 | |
392 | \sa QDBusUtil::isValidSingleSignature(), signatureToType(), |
393 | QVariant::metaType() |
394 | */ |
395 | const char *QDBusMetaType::typeToSignature(QMetaType type) |
396 | { |
397 | // check if it's a static type |
398 | switch (type.id()) |
399 | { |
400 | case QMetaType::UChar: |
401 | return DBUS_TYPE_BYTE_AS_STRING; |
402 | |
403 | case QMetaType::Bool: |
404 | return DBUS_TYPE_BOOLEAN_AS_STRING; |
405 | |
406 | case QMetaType::Short: |
407 | return DBUS_TYPE_INT16_AS_STRING; |
408 | |
409 | case QMetaType::UShort: |
410 | return DBUS_TYPE_UINT16_AS_STRING; |
411 | |
412 | case QMetaType::Int: |
413 | return DBUS_TYPE_INT32_AS_STRING; |
414 | |
415 | case QMetaType::UInt: |
416 | return DBUS_TYPE_UINT32_AS_STRING; |
417 | |
418 | case QMetaType::LongLong: |
419 | return DBUS_TYPE_INT64_AS_STRING; |
420 | |
421 | case QMetaType::ULongLong: |
422 | return DBUS_TYPE_UINT64_AS_STRING; |
423 | |
424 | case QMetaType::Double: |
425 | return DBUS_TYPE_DOUBLE_AS_STRING; |
426 | |
427 | case QMetaType::QString: |
428 | return DBUS_TYPE_STRING_AS_STRING; |
429 | |
430 | case QMetaType::QStringList: |
431 | return DBUS_TYPE_ARRAY_AS_STRING |
432 | DBUS_TYPE_STRING_AS_STRING; // as |
433 | |
434 | case QMetaType::QByteArray: |
435 | return DBUS_TYPE_ARRAY_AS_STRING |
436 | DBUS_TYPE_BYTE_AS_STRING; // ay |
437 | } |
438 | |
439 | QDBusMetaTypeId::init(); |
440 | if (type == QDBusMetaTypeId::variant()) |
441 | return DBUS_TYPE_VARIANT_AS_STRING; |
442 | else if (type == QDBusMetaTypeId::objectpath()) |
443 | return DBUS_TYPE_OBJECT_PATH_AS_STRING; |
444 | else if (type == QDBusMetaTypeId::signature()) |
445 | return DBUS_TYPE_SIGNATURE_AS_STRING; |
446 | else if (type == QDBusMetaTypeId::unixfd()) |
447 | return DBUS_TYPE_UNIX_FD_AS_STRING; |
448 | |
449 | // try the database |
450 | auto *ct = customTypes(); |
451 | if (!ct) |
452 | return nullptr; |
453 | |
454 | { |
455 | QReadLocker locker(&ct->lock); |
456 | auto it = ct->hash.constFind(key: type.id()); |
457 | if (it == ct->hash.end()) |
458 | return nullptr; |
459 | |
460 | const QDBusCustomTypeInfo &info = *it; |
461 | |
462 | if (!info.signature.isNull()) |
463 | return info.signature; |
464 | |
465 | if (!info.marshall) |
466 | return nullptr; // type not registered with us |
467 | } |
468 | |
469 | // call to user code to construct the signature type |
470 | QDBusCustomTypeInfo *info; |
471 | { |
472 | // createSignature will never return a null QByteArray |
473 | // if there was an error, it'll return "" |
474 | QByteArray signature = QDBusArgumentPrivate::createSignature(id: type.id()); |
475 | |
476 | // re-acquire lock |
477 | QWriteLocker locker(&ct->lock); |
478 | info = &ct->hash[type.id()]; |
479 | info->signature = signature; |
480 | } |
481 | return info->signature; |
482 | } |
483 | |
484 | QT_END_NAMESPACE |
485 | |
486 | #endif // QT_NO_DBUS |
487 | |