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(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("::");
69 if (scopeIdx != -1) {
70 scope = scopedName.left(scopeIdx);
71 name = scopedName.mid(scopeIdx + 2);
72 } else {
73 name = scopedName;
74 }
75
76 if (scope == "Qt")
77 return isNamedEnumeratorInScope(StaticQtMetaObject::get(), scope, name);
78
79 if (isNamedEnumeratorInScope(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(*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(fromm, 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(fromm, 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(fromm, 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(_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(data.coreIndex());
205 } else {
206 m = _m.asT2()->method(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(metaObject(), 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 typedef QQmlPropertyCacheMethodArguments A;
236
237 QQmlPropertyCache *c = _m.asT1();
238 Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count());
239
240 while (index < c->methodIndexCacheStart)
241 c = c->_parent;
242
243 QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart));
244
245 if (rv->arguments() && static_cast<A *>(rv->arguments())->argumentsValid)
246 return static_cast<A *>(rv->arguments())->arguments;
247
248 const QMetaObject *metaObject = c->createMetaObject();
249 Q_ASSERT(metaObject);
250 QMetaMethod m = metaObject->method(index);
251
252 int argc = m.parameterCount();
253 if (!rv->arguments()) {
254 A *args = c->createArgumentsObject(argc, m.parameterNames());
255 rv->setArguments(args);
256 }
257 A *args = static_cast<A *>(rv->arguments());
258
259 QList<QByteArray> argTypeNames; // Only loaded if needed
260
261 for (int ii = 0; ii < argc; ++ii) {
262 int type = m.parameterType(ii);
263
264 if (QMetaType::sizeOf(type) > int(sizeof(int))) {
265 // Cannot be passed as int
266 // We know that it's a known type, as sizeOf(UnknownType) == 0
267 } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) {
268 type = QMetaType::Int;
269 } else {
270 if (argTypeNames.isEmpty())
271 argTypeNames = m.parameterTypes();
272 if (isNamedEnumerator(metaObject, argTypeNames.at(ii))) {
273 type = QMetaType::Int;
274 } else if (type == QMetaType::UnknownType){
275 if (unknownTypeError)
276 *unknownTypeError = argTypeNames.at(ii);
277 return nullptr;
278 }
279
280 }
281 args->arguments[ii + 1] = type;
282 }
283 args->argumentsValid = true;
284 return static_cast<A *>(rv->arguments())->arguments;
285
286 } else {
287 QMetaMethod m = _m.asT2()->method(index);
288 return methodParameterTypes(m, argStorage, unknownTypeError);
289
290 }
291}
292
293int *QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage *argStorage,
294 QByteArray *unknownTypeError) const
295{
296 Q_ASSERT(argStorage);
297
298 int argc = m.parameterCount();
299 argStorage->resize(argc + 1);
300 argStorage->operator[](0) = argc;
301 QList<QByteArray> argTypeNames; // Only loaded if needed
302
303 for (int ii = 0; ii < argc; ++ii) {
304 int type = m.parameterType(ii);
305 if (QMetaType::sizeOf(type) > int(sizeof(int))) {
306 // Cannot be passed as int
307 // We know that it's a known type, as sizeOf(UnknownType) == 0
308 } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) {
309 type = QMetaType::Int;
310 } else {
311 if (argTypeNames.isEmpty())
312 argTypeNames = m.parameterTypes();
313 if (isNamedEnumerator(_m.asT2(), argTypeNames.at(ii))) {
314 type = QMetaType::Int;
315 } else if (type == QMetaType::UnknownType) {
316 if (unknownTypeError)
317 *unknownTypeError = argTypeNames.at(ii);
318 return nullptr;
319 }
320 }
321 argStorage->operator[](ii + 1) = type;
322 }
323
324 return argStorage->data();
325}
326
327QT_END_NAMESPACE
328