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