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
qdbusxmlgenerator.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
5#include <QtCore/qmetaobject.h>
6#include <QtCore/qstringlist.h>
7#include <QtCore/qdebug.h>
8
9#include "qdbusinterface_p.h" // for ANNOTATION_NO_WAIT
10#include "qdbusabstractadaptor_p.h" // for QCLASSINFO_DBUS_*
11#include "qdbusconnection_p.h" // for the flags
13#include "qdbusmetatype.h"
14#include "qdbusutil_p.h"
15
16#ifndef QT_NO_DBUS
17
19
20using namespace Qt::StringLiterals;
21
22extern Q_DBUS_EXPORT QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
23 const QMetaObject *base, int flags);
24
25static inline QString typeNameToXml(const char *typeName)
26{
27 // ### copied from qtextdocument.cpp
28 // ### move this into Qt Core at some point
29 const QLatin1StringView plain(typeName);
30 QString rich;
31 rich.reserve(int(plain.size() * 1.1));
32 for (int i = 0; i < plain.size(); ++i) {
33 if (plain.at(i) == u'<')
34 rich += "&lt;"_L1;
35 else if (plain.at(i) == u'>')
36 rich += "&gt;"_L1;
37 else if (plain.at(i) == u'&')
38 rich += "&amp;"_L1;
39 else
40 rich += plain.at(i);
41 }
42 return rich;
43}
44
45static inline QLatin1StringView accessAsString(bool read, bool write)
46{
47 if (read)
48 return write ? "readwrite"_L1 : "read"_L1 ;
49 else
50 return write ? "write"_L1 : ""_L1 ;
51}
52
53// implement the D-Bus org.freedesktop.DBus.Introspectable interface
54// we do that by analysing the metaObject of all the adaptor interfaces
55
56static QString generateInterfaceXml(const QMetaObject *mo, int flags, int methodOffset, int propOffset)
57{
58 QString retval;
59
60 // start with properties:
61 if (flags & (QDBusConnection::ExportScriptableProperties |
62 QDBusConnection::ExportNonScriptableProperties)) {
63 for (int i = propOffset; i < mo->propertyCount(); ++i) {
64
65 QMetaProperty mp = mo->property(i);
66
67 if (!((mp.isScriptable() && (flags & QDBusConnection::ExportScriptableProperties)) ||
68 (!mp.isScriptable() && (flags & QDBusConnection::ExportNonScriptableProperties))))
69 continue;
70
71 QMetaType type = mp.metaType();
72 if (!type.isValid())
73 continue;
74 const char *signature = QDBusMetaType::typeToSignature(type);
75 if (!signature)
76 continue;
77
78 retval += " <property name=\"%1\" type=\"%2\" access=\"%3\""_L1
79 .arg(QLatin1StringView(mp.name()),
80 QLatin1StringView(signature),
81 accessAsString(mp.isReadable(), mp.isWritable()));
82
83 if (!QDBusMetaType::signatureToMetaType(signature).isValid()) {
84 const char *typeName = type.name();
85 retval += ">\n <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"%3\"/>\n </property>\n"_L1
86 .arg(typeNameToXml(typeName));
87 } else {
88 retval += "/>\n"_L1;
89 }
90 }
91 }
92
93 // now add methods:
94 for (int i = methodOffset; i < mo->methodCount(); ++i) {
95 QMetaMethod mm = mo->method(i);
96
97 bool isSignal = false;
98 bool isSlot = false;
99 if (mm.methodType() == QMetaMethod::Signal)
100 // adding a signal
101 isSignal = true;
102 else if (mm.access() == QMetaMethod::Public && mm.methodType() == QMetaMethod::Slot)
103 isSlot = true;
104 else if (mm.access() == QMetaMethod::Public && mm.methodType() == QMetaMethod::Method)
105 ; // invokable, neither signal nor slot
106 else
107 continue; // neither signal nor public method/slot
108
109 if (isSignal && !(flags & (QDBusConnection::ExportScriptableSignals |
110 QDBusConnection::ExportNonScriptableSignals)))
111 continue; // we're not exporting any signals
112 if (!isSignal && (!(flags & (QDBusConnection::ExportScriptableSlots | QDBusConnection::ExportNonScriptableSlots)) &&
113 !(flags & (QDBusConnection::ExportScriptableInvokables | QDBusConnection::ExportNonScriptableInvokables))))
114 continue; // we're not exporting any slots or invokables
115
116 // we want to skip non-scriptable stuff as early as possible to avoid bogus warning
117 // for methods that are not being exported at all
118 bool isScriptable = mm.attributes() & QMetaMethod::Scriptable;
119 if (!isScriptable && !(flags & (isSignal ? QDBusConnection::ExportNonScriptableSignals : QDBusConnection::ExportNonScriptableInvokables | QDBusConnection::ExportNonScriptableSlots)))
120 continue;
121
122 QString xml = QString::asprintf(" <%s name=\"%s\">\n",
123 isSignal ? "signal" : "method", mm.name().constData());
124
125 // check the return type first
126 QMetaType typeId = mm.returnMetaType();
127 const bool hasVoidReturn = typeId.id() == QMetaType::Void;
128 if (typeId.isValid() && !hasVoidReturn) {
129 const char *typeName = QDBusMetaType::typeToSignature(typeId);
130 if (typeName) {
131 xml += " <arg type=\"%1\" direction=\"out\"/>\n"_L1
132 .arg(typeNameToXml(typeName));
133
134 // do we need to describe this argument?
135 if (!QDBusMetaType::signatureToMetaType(typeName).isValid())
136 xml += " <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"%1\"/>\n"_L1
137 .arg(typeNameToXml(QMetaType(typeId).name()));
138 } else {
139 qWarning() << "Unsupported return type" << typeId.id() << typeId.name() << "in method" << mm.name();
140 continue;
141 }
142 } else if (!typeId.isValid()) {
143 qWarning() << "Invalid return type in method" << mm.name();
144 continue; // wasn't a valid type
145 }
146
147 QList<QByteArray> names = mm.parameterNames();
148 QList<QMetaType> types;
149 QString errorMsg;
150 int inputCount = qDBusParametersForMethod(mm, types, errorMsg);
151 const int outputArgsStart = hasVoidReturn ? 0 : 1;
152 if (inputCount == -1) {
153 qWarning() << "Skipped method" << mm.name() << ":" << qPrintable(errorMsg);
154 continue; // invalid form
155 }
156 if (isSignal && inputCount + 1 != types.size())
157 continue; // signal with output arguments?
158 if (isSignal && types.at(inputCount) == QDBusMetaTypeId::message())
159 continue; // signal with QDBusMessage argument?
160 if (isSignal && mm.attributes() & QMetaMethod::Cloned)
161 continue; // cloned signal?
162
163 int j;
164 for (j = 1; j < types.size(); ++j) {
165 // input parameter for a slot or output for a signal
166 if (types.at(j) == QDBusMetaTypeId::message()) {
167 isScriptable = true;
168 continue;
169 }
170
171 QString name;
172 if (!names.at(j - 1).isEmpty())
173 name = "name=\"%1\" "_L1.arg(QLatin1StringView(names.at(j - 1)));
174
175 bool isOutput = isSignal || j > inputCount;
176
177 const char *signature = QDBusMetaType::typeToSignature(types.at(j));
178 xml += QString::asprintf(" <arg %lstype=\"%s\" direction=\"%s\"/>\n",
179 qUtf16Printable(name), signature, isOutput ? "out" : "in");
180
181 // do we need to describe this argument?
182 if (!QDBusMetaType::signatureToMetaType(signature).isValid()) {
183 const char *typeName = QMetaType(types.at(j)).name();
184 xml += QString::fromLatin1(" <annotation name=\"org.qtproject.QtDBus.QtTypeName.%1%2\" value=\"%3\"/>\n")
185 .arg(isOutput ? "Out"_L1 : "In"_L1)
186 .arg(isOutput && !isSignal ? j - 1 - inputCount + outputArgsStart : j - 1)
187 .arg(typeNameToXml(typeName));
188 }
189 }
190
191 int wantedMask;
192 if (isScriptable)
193 wantedMask = isSignal ? QDBusConnection::ExportScriptableSignals
194 : isSlot ? QDBusConnection::ExportScriptableSlots
195 : QDBusConnection::ExportScriptableInvokables;
196 else
197 wantedMask = isSignal ? QDBusConnection::ExportNonScriptableSignals
198 : isSlot ? QDBusConnection::ExportNonScriptableSlots
199 : QDBusConnection::ExportNonScriptableInvokables;
200 if ((flags & wantedMask) != wantedMask)
201 continue;
202
203 if (qDBusCheckAsyncTag(mm.tag()))
204 // add the no-reply annotation
205 xml += " <annotation name=\"" ANNOTATION_NO_WAIT "\" value=\"true\"/>\n"_L1;
206
207 retval += xml;
208 retval += " </%1>\n"_L1.arg(isSignal ? "signal"_L1 : "method"_L1);
209 }
210
211 return retval;
212}
213
214QString qDBusGenerateMetaObjectXml(QString interface, const QMetaObject *mo,
215 const QMetaObject *base, int flags)
216{
217 if (interface.isEmpty())
218 // generate the interface name from the meta object
219 interface = qDBusInterfaceFromMetaObject(mo);
220
221 QString xml;
222 int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTROSPECTION);
223 if (idx >= mo->classInfoOffset())
224 return QString::fromUtf8(mo->classInfo(idx).value());
225 else
226 xml = generateInterfaceXml(mo, flags, base->methodCount(), base->propertyCount());
227
228 if (xml.isEmpty())
229 return QString(); // don't add an empty interface
230 return " <interface name=\"%1\">\n%2 </interface>\n"_L1
231 .arg(interface, xml);
232}
233
234QT_END_NAMESPACE
235
236#endif // QT_NO_DBUS
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:177
Combined button and popup list for selecting options.
#define QCLASSINFO_DBUS_INTROSPECTION
#define ANNOTATION_NO_WAIT
static QLatin1StringView accessAsString(bool read, bool write)
static QString generateInterfaceXml(const QMetaObject *mo, int flags, int methodOffset, int propOffset)
static QString typeNameToXml(const char *typeName)