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 {
getStaticQtMetaObject49     static const QMetaObject *get()
50     { return &staticQtMetaObject; }
51 };
52 
isNamedEnumeratorInScope(const QMetaObject * resolvedMetaObject,const QByteArray & scope,const QByteArray & name)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(i);
58         if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
59             return true;
60     }
61     return false;
62 }
63 
isNamedEnumerator(const QMetaObject * metaObj,const QByteArray & scopedName)64 static 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
canConvert(const QQmlMetaObject & from,const QQmlMetaObject & 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(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 
resolveGadgetMethodOrPropertyIndex(QMetaObject::Call type,const QMetaObject ** metaObject,int * index)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 
propertyCache(QQmlEnginePrivate * e) const176 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(_m.asT2());
181 }
182 
methodReturnType(const QQmlPropertyData & data,QByteArray * unknownTypeError) const183 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(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 
methodParameterTypes(int index,ArgTypeStorage * argStorage,QByteArray * unknownTypeError) const229 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         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 
methodParameterTypes(const QMetaMethod & m,ArgTypeStorage * argStorage,QByteArray * unknownTypeError) const293 int *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 
327 QT_END_NAMESPACE
328