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
34QT_BEGIN_NAMESPACE
35
36class QDBusCustomTypeInfo
37{
38public:
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
49void 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
102struct QDBusCustomTypes
103{
104 QReadWriteLock lock;
105 QHash<int, QDBusCustomTypeInfo> hash;
106};
107
108Q_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*/
185void 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*/
208bool 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*/
243bool 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*/
290QMetaType 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*/
372void 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*/
395const 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
484QT_END_NAMESPACE
485
486#endif // QT_NO_DBUS
487

source code of qtbase/src/dbus/qdbusmetatype.cpp