1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2019 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml 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 "qqmlmetaobject_p.h" |
41 | |
42 | #include <private/qqmlengine_p.h> |
43 | #include <private/qqmlpropertycachemethodarguments_p.h> |
44 | |
45 | QT_BEGIN_NAMESPACE |
46 | |
47 | struct StaticQtMetaObject : public QObject |
48 | { |
49 | static const QMetaObject *get() |
50 | { return &staticQtMetaObject; } |
51 | }; |
52 | |
53 | static bool isNamedEnumeratorInScope(const QMetaObject *resolvedMetaObject, const QByteArray &scope, |
54 | const QByteArray &name) |
55 | { |
56 | for (int i = resolvedMetaObject->enumeratorCount() - 1; i >= 0; --i) { |
57 | QMetaEnum m = resolvedMetaObject->enumerator(index: i); |
58 | if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) |
59 | return true; |
60 | } |
61 | return false; |
62 | } |
63 | |
64 | static bool isNamedEnumerator(const QMetaObject *metaObj, const QByteArray &scopedName) |
65 | { |
66 | QByteArray scope; |
67 | QByteArray name; |
68 | int scopeIdx = scopedName.lastIndexOf(c: "::" ); |
69 | if (scopeIdx != -1) { |
70 | scope = scopedName.left(len: scopeIdx); |
71 | name = scopedName.mid(index: scopeIdx + 2); |
72 | } else { |
73 | name = scopedName; |
74 | } |
75 | |
76 | if (scope == "Qt" ) |
77 | return isNamedEnumeratorInScope(resolvedMetaObject: StaticQtMetaObject::get(), scope, name); |
78 | |
79 | if (isNamedEnumeratorInScope(resolvedMetaObject: metaObj, scope, name)) |
80 | return true; |
81 | |
82 | if (metaObj->d.relatedMetaObjects && !scope.isEmpty()) { |
83 | for (auto related = metaObj->d.relatedMetaObjects; *related; ++related) { |
84 | if (isNamedEnumeratorInScope(resolvedMetaObject: *related, scope, name)) |
85 | return true; |
86 | } |
87 | } |
88 | |
89 | return false; |
90 | } |
91 | |
92 | // Returns true if \a from is assignable to a property of type \a to |
93 | bool QQmlMetaObject::canConvert(const QQmlMetaObject &from, const QQmlMetaObject &to) |
94 | { |
95 | Q_ASSERT(!from.isNull() && !to.isNull()); |
96 | |
97 | struct I { static bool equal(const QMetaObject *lhs, const QMetaObject *rhs) { |
98 | return lhs == rhs || (lhs && rhs && lhs->d.stringdata == rhs->d.stringdata); |
99 | } }; |
100 | |
101 | const QMetaObject *tom = to._m.isT1()?to._m.asT1()->metaObject():to._m.asT2(); |
102 | if (tom == &QObject::staticMetaObject) return true; |
103 | |
104 | if (from._m.isT1() && to._m.isT1()) { // QQmlPropertyCache -> QQmlPropertyCache |
105 | QQmlPropertyCache *fromp = from._m.asT1(); |
106 | QQmlPropertyCache *top = to._m.asT1(); |
107 | |
108 | while (fromp) { |
109 | if (fromp == top) return true; |
110 | fromp = fromp->parent(); |
111 | } |
112 | } else if (from._m.isT1() && to._m.isT2()) { // QQmlPropertyCache -> QMetaObject |
113 | QQmlPropertyCache *fromp = from._m.asT1(); |
114 | |
115 | while (fromp) { |
116 | const QMetaObject *fromm = fromp->metaObject(); |
117 | if (fromm && I::equal(lhs: fromm, rhs: tom)) return true; |
118 | fromp = fromp->parent(); |
119 | } |
120 | } else if (from._m.isT2() && to._m.isT1()) { // QMetaObject -> QQmlPropertyCache |
121 | const QMetaObject *fromm = from._m.asT2(); |
122 | |
123 | if (!tom) return false; |
124 | |
125 | while (fromm) { |
126 | if (I::equal(lhs: fromm, rhs: tom)) return true; |
127 | fromm = fromm->superClass(); |
128 | } |
129 | } else { // QMetaObject -> QMetaObject |
130 | const QMetaObject *fromm = from._m.asT2(); |
131 | |
132 | while (fromm) { |
133 | if (I::equal(lhs: fromm, rhs: tom)) return true; |
134 | fromm = fromm->superClass(); |
135 | } |
136 | } |
137 | |
138 | return false; |
139 | } |
140 | |
141 | void QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type, const QMetaObject **metaObject, int *index) |
142 | { |
143 | int offset; |
144 | |
145 | switch (type) { |
146 | case QMetaObject::ReadProperty: |
147 | case QMetaObject::WriteProperty: |
148 | case QMetaObject::ResetProperty: |
149 | case QMetaObject::QueryPropertyDesignable: |
150 | case QMetaObject::QueryPropertyEditable: |
151 | case QMetaObject::QueryPropertyScriptable: |
152 | case QMetaObject::QueryPropertyStored: |
153 | case QMetaObject::QueryPropertyUser: |
154 | offset = (*metaObject)->propertyOffset(); |
155 | while (*index < offset) { |
156 | *metaObject = (*metaObject)->superClass(); |
157 | offset = (*metaObject)->propertyOffset(); |
158 | } |
159 | break; |
160 | case QMetaObject::InvokeMetaMethod: |
161 | offset = (*metaObject)->methodOffset(); |
162 | while (*index < offset) { |
163 | *metaObject = (*metaObject)->superClass(); |
164 | offset = (*metaObject)->methodOffset(); |
165 | } |
166 | break; |
167 | default: |
168 | offset = 0; |
169 | Q_UNIMPLEMENTED(); |
170 | offset = INT_MAX; |
171 | } |
172 | |
173 | *index -= offset; |
174 | } |
175 | |
176 | QQmlPropertyCache *QQmlMetaObject::propertyCache(QQmlEnginePrivate *e) const |
177 | { |
178 | if (_m.isNull()) return nullptr; |
179 | if (_m.isT1()) return _m.asT1(); |
180 | else return e->cache(metaObject: _m.asT2()); |
181 | } |
182 | |
183 | int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *unknownTypeError) const |
184 | { |
185 | Q_ASSERT(!_m.isNull() && data.coreIndex() >= 0); |
186 | |
187 | int type = data.propType(); |
188 | |
189 | const char *propTypeName = nullptr; |
190 | |
191 | if (type == QMetaType::UnknownType) { |
192 | // Find the return type name from the method info |
193 | QMetaMethod m; |
194 | |
195 | if (_m.isT1()) { |
196 | QQmlPropertyCache *c = _m.asT1(); |
197 | Q_ASSERT(data.coreIndex() < c->methodIndexCacheStart + c->methodIndexCache.count()); |
198 | |
199 | while (data.coreIndex() < c->methodIndexCacheStart) |
200 | c = c->_parent; |
201 | |
202 | const QMetaObject *metaObject = c->createMetaObject(); |
203 | Q_ASSERT(metaObject); |
204 | m = metaObject->method(index: data.coreIndex()); |
205 | } else { |
206 | m = _m.asT2()->method(index: data.coreIndex()); |
207 | } |
208 | |
209 | type = m.returnType(); |
210 | propTypeName = m.typeName(); |
211 | } |
212 | |
213 | if (QMetaType::sizeOf(type) <= int(sizeof(int))) { |
214 | if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) |
215 | return QMetaType::Int; |
216 | |
217 | if (isNamedEnumerator(metaObj: metaObject(), scopedName: propTypeName)) |
218 | return QMetaType::Int; |
219 | |
220 | if (type == QMetaType::UnknownType) { |
221 | if (unknownTypeError) |
222 | *unknownTypeError = propTypeName; |
223 | } |
224 | } // else we know that it's a known type, as sizeOf(UnknownType) == 0 |
225 | |
226 | return type; |
227 | } |
228 | |
229 | int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, |
230 | QByteArray *unknownTypeError) const |
231 | { |
232 | Q_ASSERT(!_m.isNull() && index >= 0); |
233 | |
234 | if (_m.isT1()) { |
235 | QQmlPropertyCache *c = _m.asT1(); |
236 | Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count()); |
237 | |
238 | while (index < c->methodIndexCacheStart) |
239 | c = c->_parent; |
240 | |
241 | QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(i: index - c->methodIndexCacheStart)); |
242 | |
243 | if (QQmlPropertyCacheMethodArguments *args = rv->arguments()) |
244 | return args->arguments; |
245 | |
246 | const QMetaObject *metaObject = c->createMetaObject(); |
247 | Q_ASSERT(metaObject); |
248 | QMetaMethod m = metaObject->method(index); |
249 | |
250 | int argc = m.parameterCount(); |
251 | |
252 | QQmlPropertyCacheMethodArguments *args = c->createArgumentsObject(count: argc, names: m.parameterNames()); |
253 | |
254 | QList<QByteArray> argTypeNames; // Only loaded if needed |
255 | |
256 | for (int ii = 0; ii < argc; ++ii) { |
257 | int type = m.parameterType(index: ii); |
258 | |
259 | if (QMetaType::sizeOf(type) > int(sizeof(int))) { |
260 | // Cannot be passed as int |
261 | // We know that it's a known type, as sizeOf(UnknownType) == 0 |
262 | } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) { |
263 | type = QMetaType::Int; |
264 | } else { |
265 | if (argTypeNames.isEmpty()) |
266 | argTypeNames = m.parameterTypes(); |
267 | if (isNamedEnumerator(metaObj: metaObject, scopedName: argTypeNames.at(i: ii))) { |
268 | type = QMetaType::Int; |
269 | } else if (type == QMetaType::UnknownType){ |
270 | if (unknownTypeError) |
271 | *unknownTypeError = argTypeNames.at(i: ii); |
272 | return nullptr; |
273 | } |
274 | |
275 | } |
276 | args->arguments[ii + 1] = type; |
277 | } |
278 | |
279 | // If we cannot set it, then another thread has set it in the mean time. |
280 | // Just return that one, then. We don't have to delete the arguments object |
281 | // we've created as it's tracked in a linked list. |
282 | if (rv->setArguments(args)) |
283 | return args->arguments; |
284 | else |
285 | return rv->arguments()->arguments; |
286 | |
287 | } else { |
288 | QMetaMethod m = _m.asT2()->method(index); |
289 | return methodParameterTypes(method: m, argStorage, unknownTypeError); |
290 | |
291 | } |
292 | } |
293 | |
294 | int *QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage, |
295 | QByteArray *unknownTypeError) const |
296 | { |
297 | Q_ASSERT(argStorage); |
298 | |
299 | int argc = m.parameterCount(); |
300 | argStorage->resize(asize: argc + 1); |
301 | argStorage->operator[](idx: 0) = argc; |
302 | QList<QByteArray> argTypeNames; // Only loaded if needed |
303 | |
304 | for (int ii = 0; ii < argc; ++ii) { |
305 | int type = m.parameterType(index: ii); |
306 | if (QMetaType::sizeOf(type) > int(sizeof(int))) { |
307 | // Cannot be passed as int |
308 | // We know that it's a known type, as sizeOf(UnknownType) == 0 |
309 | } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) { |
310 | type = QMetaType::Int; |
311 | } else { |
312 | if (argTypeNames.isEmpty()) |
313 | argTypeNames = m.parameterTypes(); |
314 | if (isNamedEnumerator(metaObj: _m.asT2(), scopedName: argTypeNames.at(i: ii))) { |
315 | type = QMetaType::Int; |
316 | } else if (type == QMetaType::UnknownType) { |
317 | if (unknownTypeError) |
318 | *unknownTypeError = argTypeNames.at(i: ii); |
319 | return nullptr; |
320 | } |
321 | } |
322 | argStorage->operator[](idx: ii + 1) = type; |
323 | } |
324 | |
325 | return argStorage->data(); |
326 | } |
327 | |
328 | QT_END_NAMESPACE |
329 | |