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 | |
47 | QT_BEGIN_NAMESPACE |
48 | |
49 | namespace QtPrivate { |
50 | |
51 | //Based on compile time checks for static connect() from qobjectdefs_impl.h |
52 | template <class ObjectType, typename Func1, typename Func2> |
53 | static 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 | |
68 | template <class ObjectType, typename Func1, typename Func2> |
69 | static 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 | |
87 | template <class ObjectType, typename Func1, typename Func2> |
88 | static 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. |
107 | inline 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 | |
113 | inline 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 | |
135 | struct 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 | |
152 | template <class ObjectType, typename Func1, typename Func2> |
153 | static 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 | |
196 | template <class ObjectType> |
197 | static 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 | |
203 | QByteArray qtro_classinfo_signature(const QMetaObject *metaObject); |
204 | |
205 | } |
206 | |
207 | // TODO ModelInfo just needs roles, and no need for SubclassInfo |
208 | class QAbstractItemModel; |
209 | |
210 | struct ModelInfo |
211 | { |
212 | QAbstractItemModel *ptr; |
213 | QString name; |
214 | QByteArray roles; |
215 | }; |
216 | |
217 | class SourceApiMap |
218 | { |
219 | protected: |
220 | SourceApiMap() {} |
221 | public: |
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 | |
255 | QT_END_NAMESPACE |
256 | |
257 | #endif |
258 | |