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
45QT_BEGIN_NAMESPACE
46
47struct StaticQtMetaObject : public QObject
48{
49 static const QMetaObject *get()
50 { return &staticQtMetaObject; }
51};
52
53static 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
64static 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
93bool 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
141void 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
176QQmlPropertyCache *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
183int 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
229int *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
294int *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
328QT_END_NAMESPACE
329

source code of qtdeclarative/src/qml/qml/qqmlmetaobject.cpp