1/****************************************************************************
2**
3** Copyright (C) 2017 Ford Motor Company
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtRemoteObjects 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#ifndef QREMOTEOBJECTSOURCE_H
41#define QREMOTEOBJECTSOURCE_H
42
43#include <QtCore/qscopedpointer.h>
44#include <QtRemoteObjects/qtremoteobjectglobal.h>
45#include <QtCore/qmetaobject.h>
46
47QT_BEGIN_NAMESPACE
48
49namespace QtPrivate {
50
51//Based on compile time checks for static connect() from qobjectdefs_impl.h
52template <class ObjectType, typename Func1, typename Func2>
53static inline int qtro_property_index(Func1, Func2, const char *propName)
54{
55 typedef QtPrivate::FunctionPointer<Func1> Type1;
56 typedef QtPrivate::FunctionPointer<Func2> Type2;
57
58 //compilation error if the arguments do not match.
59 Q_STATIC_ASSERT_X(int(Type1::ArgumentCount) >= int(Type2::ArgumentCount),
60 "Argument counts are not compatible.");
61 Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename Type1::Arguments, typename Type2::Arguments>::value),
62 "Arguments are not compatible.");
63 Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename Type1::ReturnType, typename Type2::ReturnType>::value),
64 "Return types are not compatible.");
65 return ObjectType::staticMetaObject.indexOfProperty(propName);
66}
67
68template <class ObjectType, typename Func1, typename Func2>
69static inline int qtro_signal_index(Func1 func, Func2, int *count, int const **types)
70{
71 typedef QtPrivate::FunctionPointer<Func1> Type1;
72 typedef QtPrivate::FunctionPointer<Func2> Type2;
73
74 //compilation error if the arguments do not match.
75 Q_STATIC_ASSERT_X(int(Type1::ArgumentCount) >= int(Type2::ArgumentCount),
76 "Argument counts are not compatible.");
77 Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename Type1::Arguments, typename Type2::Arguments>::value),
78 "Arguments are not compatible.");
79 Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename Type1::ReturnType, typename Type2::ReturnType>::value),
80 "Return types are not compatible.");
81 const QMetaMethod sig = QMetaMethod::fromSignal(func);
82 *count = Type2::ArgumentCount;
83 *types = QtPrivate::ConnectionTypes<typename Type2::Arguments>::types();
84 return sig.methodIndex();
85}
86
87template <class ObjectType, typename Func1, typename Func2>
88static inline void qtro_method_test(Func1, Func2)
89{
90 typedef QtPrivate::FunctionPointer<Func1> Type1;
91 typedef QtPrivate::FunctionPointer<Func2> Type2;
92
93 //compilation error if the arguments do not match.
94 Q_STATIC_ASSERT_X(int(Type1::ArgumentCount) >= int(Type2::ArgumentCount),
95 "Argument counts are not compatible.");
96 Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename Type1::Arguments, typename Type2::Arguments>::value),
97 "Arguments are not compatible.");
98 Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename Type1::ReturnType, typename Type2::ReturnType>::value),
99 "Return types are not compatible.");
100}
101
102// The stringData, methodMatch and QMetaObjectPrivate methods are modified versions of the code
103// from qmetaobject_p.h/qmetaobject.cpp. The modifications are based on our custom need to match
104// a method name that comes from the .rep file.
105// The QMetaObjectPrivate struct should only have members appended to maintain binary compatibility,
106// so we should be fine with only the listed version with the fields we use.
107inline const QByteArray apiStringData(const QMetaObject *mo, int index)
108{
109 const QByteArrayDataPtr data = { .ptr: const_cast<QByteArrayData*>(&mo->d.stringdata[index]) };
110 return data;
111}
112
113inline bool apiMethodMatch(const QMetaObject *m, int handle,
114 const QByteArray &name, int argc,
115 const int *types)
116{
117 if (int(m->d.data[handle + 1]) != argc)
118 return false;
119 if (apiStringData(mo: m, index: m->d.data[handle]) != name)
120 return false;
121 int paramsIndex = m->d.data[handle + 2] + 1;
122 for (int i = 0; i < argc; ++i) {
123 uint typeInfo = m->d.data[paramsIndex + i];
124 if (typeInfo & 0x80000000) { // Custom/named type, compare names
125 const char *t = QMetaType::typeName(type: types[i]);
126 const auto type = QByteArray::fromRawData(t, size: qstrlen(str: t));
127 if (type != apiStringData(mo: m, index: typeInfo & 0x7FFFFFFF))
128 return false;
129 } else if (types[i] != int(typeInfo))
130 return false;
131 }
132 return true;
133}
134
135struct QMetaObjectPrivate
136{
137 // revision 7 is Qt 5.0 everything lower is not supported
138 // revision 8 is Qt 5.12: It adds the enum name to QMetaEnum
139 enum { OutputRevision = 8 }; // Used by moc, qmetaobjectbuilder and qdbus
140
141 int revision;
142 int className;
143 int classInfoCount, classInfoData;
144 int methodCount, methodData;
145 int propertyCount, propertyData;
146 int enumeratorCount, enumeratorData;
147 int constructorCount, constructorData;
148 int flags;
149 int signalCount;
150};
151
152template <class ObjectType, typename Func1, typename Func2>
153static inline int qtro_method_index(Func1, Func2, const char *methodName, int *count, int const **types)
154{
155 typedef QtPrivate::FunctionPointer<Func1> Type1;
156 typedef QtPrivate::FunctionPointer<Func2> Type2;
157
158 //compilation error if the arguments do not match.
159 Q_STATIC_ASSERT_X(int(Type1::ArgumentCount) >= int(Type2::ArgumentCount),
160 "Argument counts are not compatible.");
161 Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename Type1::Arguments, typename Type2::Arguments>::value),
162 "Arguments are not compatible.");
163 Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename Type1::ReturnType, typename Type2::ReturnType>::value),
164 "Return types are not compatible.");
165 *count = Type2::ArgumentCount;
166 *types = QtPrivate::ConnectionTypes<typename Type2::Arguments>::types();
167
168 int result = ObjectType::staticMetaObject.indexOfMethod(methodName);
169 if (result >= 0)
170 return result;
171 // We can have issues, specifically with enums, since the compiler can infer the class. Since
172 // indexOfMethod() is doing string comparisons for registered types, "MyEnum" and "MyClass::MyEnum"
173 // won't match.
174 // Below is similar to QMetaObject->indexOfMethod, but template magic has already matched parameter
175 // types, so we need to find a match for the API method name + parameters. Neither approach works
176 // 100%, as the below code doesn't match a parameter of type "size_t" (which the template match
177 // identifies as "ulong"). These subtleties can cause the below string comparison fails.
178 // There is no known case that would fail both methods.
179 // TODO: is there a way to make this a constexpr so a failure is detected at compile time?
180 int nameLength = strchr(s: methodName, c: '(') - methodName;
181 const auto name = QByteArray::fromRawData(methodName, size: nameLength);
182 for (const QMetaObject *m = &ObjectType::staticMetaObject; m; m = m->d.superdata) {
183 const auto priv = reinterpret_cast<const QMetaObjectPrivate*>(m->d.data);
184 int i = (priv->methodCount - 1);
185 const int end = priv->signalCount;
186 for (; i >= end; --i) {
187 int handle = priv->methodData + 5*i;
188 if (apiMethodMatch(m, handle, name, argc: *count, types: *types))
189 return i + m->methodOffset();
190 }
191 }
192 qWarning() << "No matching method for" << methodName << "in the provided metaclass" << ObjectType::staticMetaObject.className();
193 return -1;
194}
195
196template <class ObjectType>
197static inline QByteArray qtro_enum_signature(const char *enumName)
198{
199 const auto qme = ObjectType::staticMetaObject.enumerator(ObjectType::staticMetaObject.indexOfEnumerator(enumName));
200 return QByteArrayLiteral("1::2").replace("1", qme.scope()).replace("2", qme.name());
201}
202
203QByteArray qtro_classinfo_signature(const QMetaObject *metaObject);
204
205}
206
207// TODO ModelInfo just needs roles, and no need for SubclassInfo
208class QAbstractItemModel;
209
210struct ModelInfo
211{
212 QAbstractItemModel *ptr;
213 QString name;
214 QByteArray roles;
215};
216
217class SourceApiMap
218{
219protected:
220 SourceApiMap() {}
221public:
222 virtual ~SourceApiMap() {}
223 virtual QString name() const = 0;
224 virtual QString typeName() const = 0;
225 virtual QByteArray className() const { return typeName().toLatin1().append(s: "Source"); }
226 virtual int enumCount() const = 0;
227 virtual int propertyCount() const = 0;
228 virtual int signalCount() const = 0;
229 virtual int methodCount() const = 0;
230 virtual int sourceEnumIndex(int index) const = 0;
231 virtual int sourcePropertyIndex(int index) const = 0;
232 virtual int sourceSignalIndex(int index) const = 0;
233 virtual int sourceMethodIndex(int index) const = 0;
234 virtual int signalParameterCount(int index) const = 0;
235 virtual int signalParameterType(int sigIndex, int paramIndex) const = 0;
236 virtual const QByteArray signalSignature(int index) const = 0;
237 virtual QList<QByteArray> signalParameterNames(int index) const = 0;
238 virtual int methodParameterCount(int index) const = 0;
239 virtual int methodParameterType(int methodIndex, int paramIndex) const = 0;
240 virtual const QByteArray methodSignature(int index) const = 0;
241 virtual QMetaMethod::MethodType methodType(int index) const = 0;
242 virtual const QByteArray typeName(int index) const = 0;
243 virtual QList<QByteArray> methodParameterNames(int index) const = 0;
244 virtual int propertyIndexFromSignal(int index) const = 0;
245 virtual int propertyRawIndexFromSignal(int index) const = 0;
246 virtual QByteArray objectSignature() const = 0;
247 virtual bool isDynamic() const { return false; }
248 virtual bool isAdapterSignal(int) const { return false; }
249 virtual bool isAdapterMethod(int) const { return false; }
250 virtual bool isAdapterProperty(int) const { return false; }
251 QVector<ModelInfo> m_models;
252 QVector<SourceApiMap *> m_subclasses;
253};
254
255QT_END_NAMESPACE
256
257#endif
258

source code of qtremoteobjects/src/remoteobjects/qremoteobjectsource.h