1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 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 | #ifndef QQMLPROPERTYCACHE_P_H |
41 | #define QQMLPROPERTYCACHE_P_H |
42 | |
43 | // |
44 | // W A R N I N G |
45 | // ------------- |
46 | // |
47 | // This file is not part of the Qt API. It exists purely as an |
48 | // implementation detail. This header file may change from version to |
49 | // version without notice, or even be removed. |
50 | // |
51 | // We mean it. |
52 | // |
53 | |
54 | #include <private/qqmlrefcount_p.h> |
55 | #include <private/qflagpointer_p.h> |
56 | #include "qqmlcleanup_p.h" |
57 | #include "qqmlnotifier_p.h" |
58 | #include <private/qqmlpropertyindex_p.h> |
59 | |
60 | #include <private/qlinkedstringhash_p.h> |
61 | #include <QtCore/qvarlengtharray.h> |
62 | #include <QtCore/qvector.h> |
63 | |
64 | #include <private/qv4value_p.h> |
65 | #include <private/qqmlpropertydata_p.h> |
66 | #include <private/qqmlenumdata_p.h> |
67 | #include <private/qqmlenumvalue_p.h> |
68 | |
69 | #include <limits> |
70 | |
71 | QT_BEGIN_NAMESPACE |
72 | |
73 | class QCryptographicHash; |
74 | class QJSEngine; |
75 | class QMetaObjectBuilder; |
76 | class QQmlVMEMetaObject; |
77 | class QQmlPropertyCacheMethodArguments; |
78 | |
79 | class Q_QML_PRIVATE_EXPORT QQmlPropertyCache : public QQmlRefCount |
80 | { |
81 | public: |
82 | QQmlPropertyCache(); |
83 | QQmlPropertyCache(const QMetaObject *, int metaObjectRevision = 0); |
84 | ~QQmlPropertyCache() override; |
85 | |
86 | void update(const QMetaObject *); |
87 | void invalidate(const QMetaObject *); |
88 | |
89 | QQmlPropertyCache *copy(); |
90 | |
91 | QQmlPropertyCache *copyAndAppend(const QMetaObject *, |
92 | QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(), |
93 | QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(), |
94 | QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags()); |
95 | QQmlPropertyCache *copyAndAppend(const QMetaObject *, int typeMinorVersion, |
96 | QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(), |
97 | QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(), |
98 | QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags()); |
99 | |
100 | QQmlPropertyCache *copyAndReserve(int propertyCount, |
101 | int methodCount, int signalCount, int enumCount); |
102 | void appendProperty(const QString &, QQmlPropertyData::Flags flags, int coreIndex, |
103 | int propType, int revision, int notifyIndex); |
104 | void appendSignal(const QString &, QQmlPropertyData::Flags, int coreIndex, |
105 | const int *types = nullptr, const QList<QByteArray> &names = QList<QByteArray>()); |
106 | void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex, int returnType, |
107 | const QList<QByteArray> &names, const QVector<int> ¶meterTypes); |
108 | void appendEnum(const QString &, const QVector<QQmlEnumValue> &); |
109 | |
110 | const QMetaObject *metaObject() const; |
111 | const QMetaObject *createMetaObject(); |
112 | const QMetaObject *firstCppMetaObject() const; |
113 | |
114 | template<typename K> |
115 | QQmlPropertyData *property(const K &key, QObject *object, QQmlContextData *context) const |
116 | { |
117 | return findProperty(stringCache.find(key), object, context); |
118 | } |
119 | |
120 | QQmlPropertyData *property(int) const; |
121 | QQmlPropertyData *maybeUnresolvedProperty(int) const; |
122 | QQmlPropertyData *method(int) const; |
123 | QQmlPropertyData *signal(int index) const; |
124 | QQmlEnumData *qmlEnum(int) const; |
125 | int methodIndexToSignalIndex(int) const; |
126 | |
127 | QString defaultPropertyName() const; |
128 | QQmlPropertyData *defaultProperty() const; |
129 | QQmlPropertyCache *parent() const; |
130 | // is used by the Qml Designer |
131 | void setParent(QQmlPropertyCache *newParent); |
132 | |
133 | inline QQmlPropertyData *overrideData(QQmlPropertyData *) const; |
134 | inline bool isAllowedInRevision(QQmlPropertyData *) const; |
135 | |
136 | static QQmlPropertyData *property(QJSEngine *, QObject *, const QStringRef &, |
137 | QQmlContextData *, QQmlPropertyData &); |
138 | static QQmlPropertyData *property(QJSEngine *, QObject *, const QLatin1String &, |
139 | QQmlContextData *, QQmlPropertyData &); |
140 | static QQmlPropertyData *property(QJSEngine *, QObject *, const QV4::String *, |
141 | QQmlContextData *, QQmlPropertyData &); |
142 | |
143 | static QQmlPropertyData *property(QJSEngine *engine, QObject *obj, const QString &name, |
144 | QQmlContextData *context, QQmlPropertyData &local) |
145 | { |
146 | return property(engine, obj, QStringRef(&name), context, local); |
147 | } |
148 | |
149 | //see QMetaObjectPrivate::originalClone |
150 | int originalClone(int index); |
151 | static int originalClone(QObject *, int index); |
152 | |
153 | QList<QByteArray> signalParameterNames(int index) const; |
154 | static QString signalParameterStringForJS(QV4::ExecutionEngine *engine, const QList<QByteArray> ¶meterNameList, QString *errorString = nullptr); |
155 | |
156 | const char *className() const; |
157 | |
158 | inline int propertyCount() const; |
159 | inline int propertyOffset() const; |
160 | inline int methodCount() const; |
161 | inline int methodOffset() const; |
162 | inline int signalCount() const; |
163 | inline int signalOffset() const; |
164 | inline int qmlEnumCount() const; |
165 | |
166 | static bool isDynamicMetaObject(const QMetaObject *); |
167 | |
168 | void toMetaObjectBuilder(QMetaObjectBuilder &); |
169 | |
170 | inline bool callJSFactoryMethod(QObject *object, void **args) const; |
171 | |
172 | static bool determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, int *stringCount); |
173 | static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo); |
174 | |
175 | QByteArray checksum(bool *ok); |
176 | |
177 | int allowedRevision(int index) const { return allowedRevisionCache[index]; } |
178 | void setAllowedRevision(int index, int allowed) { allowedRevisionCache[index] = allowed; } |
179 | |
180 | private: |
181 | friend class QQmlEnginePrivate; |
182 | friend class QQmlCompiler; |
183 | template <typename T> friend class QQmlPropertyCacheCreator; |
184 | template <typename T> friend class QQmlPropertyCacheAliasCreator; |
185 | friend class QQmlComponentAndAliasResolver; |
186 | friend class QQmlMetaObject; |
187 | |
188 | inline QQmlPropertyCache *copy(int reserve); |
189 | |
190 | void append(const QMetaObject *, int typeMinorVersion, |
191 | QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(), |
192 | QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(), |
193 | QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags()); |
194 | |
195 | QQmlPropertyCacheMethodArguments *createArgumentsObject(int count, const QList<QByteArray> &names); |
196 | |
197 | typedef QVector<QQmlPropertyData> IndexCache; |
198 | typedef QLinkedStringMultiHash<QPair<int, QQmlPropertyData *> > StringCache; |
199 | typedef QVector<int> AllowedRevisionCache; |
200 | |
201 | QQmlPropertyData *findProperty(StringCache::ConstIterator it, QObject *, QQmlContextData *) const; |
202 | QQmlPropertyData *findProperty(StringCache::ConstIterator it, const QQmlVMEMetaObject *, QQmlContextData *) const; |
203 | |
204 | QQmlPropertyData *ensureResolved(QQmlPropertyData*) const; |
205 | |
206 | Q_NEVER_INLINE void resolve(QQmlPropertyData *) const; |
207 | void updateRecur(const QMetaObject *); |
208 | |
209 | template<typename K> |
210 | QQmlPropertyData *findNamedProperty(const K &key) const |
211 | { |
212 | StringCache::mapped_type *it = stringCache.value(key); |
213 | return it ? it->second : 0; |
214 | } |
215 | |
216 | template<typename K> |
217 | void setNamedProperty(const K &key, int index, QQmlPropertyData *data, bool isOverride) |
218 | { |
219 | stringCache.insert(key, qMakePair(x: index, y: data)); |
220 | _hasPropertyOverrides |= isOverride; |
221 | } |
222 | |
223 | int findPropType(const QQmlPropertyData *data) const; |
224 | |
225 | private: |
226 | QQmlPropertyCache *_parent; |
227 | int propertyIndexCacheStart; |
228 | int methodIndexCacheStart; |
229 | int signalHandlerIndexCacheStart; |
230 | |
231 | IndexCache propertyIndexCache; |
232 | IndexCache methodIndexCache; |
233 | IndexCache signalHandlerIndexCache; |
234 | StringCache stringCache; |
235 | AllowedRevisionCache allowedRevisionCache; |
236 | QVector<QQmlEnumData> enumCache; |
237 | |
238 | bool _hasPropertyOverrides : 1; |
239 | bool _ownMetaObject : 1; |
240 | const QMetaObject *_metaObject; |
241 | QByteArray _dynamicClassName; |
242 | QByteArray _dynamicStringData; |
243 | QString _defaultPropertyName; |
244 | QAtomicPointer<QQmlPropertyCacheMethodArguments> argumentsCache; |
245 | int _jsFactoryMethodIndex; |
246 | QByteArray _checksum; |
247 | }; |
248 | |
249 | inline QQmlPropertyData *QQmlPropertyCache::ensureResolved(QQmlPropertyData *p) const |
250 | { |
251 | // Avoid resolve() in the common case where it's already initialized and we don't |
252 | // run into a data race. resolve() checks again, with an atomic operation. |
253 | // If there is no coreIndex, there is no point in trying to resolve anything. In that |
254 | // case it's a default-constructed instance that never got load()'ed or lazyLoad()'ed. |
255 | if (p && p->coreIndex() != -1 && Q_UNLIKELY(p->m_propTypeAndRelativePropIndex == 0)) |
256 | resolve(p); |
257 | |
258 | return p; |
259 | } |
260 | |
261 | // Returns this property cache's metaObject. May be null if it hasn't been created yet. |
262 | inline const QMetaObject *QQmlPropertyCache::metaObject() const |
263 | { |
264 | return _metaObject; |
265 | } |
266 | |
267 | // Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by |
268 | // QML |
269 | inline const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const |
270 | { |
271 | while (_parent && (_metaObject == nullptr || _ownMetaObject)) |
272 | return _parent->firstCppMetaObject(); |
273 | return _metaObject; |
274 | } |
275 | |
276 | inline QQmlPropertyData *QQmlPropertyCache::property(int index) const |
277 | { |
278 | if (index < 0 || index >= (propertyIndexCacheStart + propertyIndexCache.count())) |
279 | return nullptr; |
280 | |
281 | if (index < propertyIndexCacheStart) |
282 | return _parent->property(index); |
283 | |
284 | QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&propertyIndexCache.at(i: index - propertyIndexCacheStart)); |
285 | return ensureResolved(p: rv); |
286 | } |
287 | |
288 | inline QQmlPropertyData *QQmlPropertyCache::method(int index) const |
289 | { |
290 | if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count())) |
291 | return nullptr; |
292 | |
293 | if (index < methodIndexCacheStart) |
294 | return _parent->method(index); |
295 | |
296 | QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(i: index - methodIndexCacheStart)); |
297 | return ensureResolved(p: rv); |
298 | } |
299 | |
300 | /*! \internal |
301 | \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()). |
302 | This is different from QMetaMethod::methodIndex(). |
303 | */ |
304 | inline QQmlPropertyData *QQmlPropertyCache::signal(int index) const |
305 | { |
306 | if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.count())) |
307 | return nullptr; |
308 | |
309 | if (index < signalHandlerIndexCacheStart) |
310 | return _parent->signal(index); |
311 | |
312 | QQmlPropertyData *rv = const_cast<QQmlPropertyData *>(&methodIndexCache.at(i: index - signalHandlerIndexCacheStart)); |
313 | Q_ASSERT(rv->isSignal() || rv->coreIndex() == -1); |
314 | return ensureResolved(p: rv); |
315 | } |
316 | |
317 | inline QQmlEnumData *QQmlPropertyCache::qmlEnum(int index) const |
318 | { |
319 | if (index < 0 || index >= enumCache.count()) |
320 | return nullptr; |
321 | |
322 | return const_cast<QQmlEnumData *>(&enumCache.at(i: index)); |
323 | } |
324 | |
325 | inline int QQmlPropertyCache::methodIndexToSignalIndex(int index) const |
326 | { |
327 | if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.count())) |
328 | return index; |
329 | |
330 | if (index < methodIndexCacheStart) |
331 | return _parent->methodIndexToSignalIndex(index); |
332 | |
333 | return index - methodIndexCacheStart + signalHandlerIndexCacheStart; |
334 | } |
335 | |
336 | // Returns the name of the default property for this cache |
337 | inline QString QQmlPropertyCache::defaultPropertyName() const |
338 | { |
339 | return _defaultPropertyName; |
340 | } |
341 | |
342 | inline QQmlPropertyCache *QQmlPropertyCache::parent() const |
343 | { |
344 | return _parent; |
345 | } |
346 | |
347 | QQmlPropertyData * |
348 | QQmlPropertyCache::overrideData(QQmlPropertyData *data) const |
349 | { |
350 | if (!data->hasOverride()) |
351 | return nullptr; |
352 | |
353 | if (data->overrideIndexIsProperty()) |
354 | return property(index: data->overrideIndex()); |
355 | else |
356 | return method(index: data->overrideIndex()); |
357 | } |
358 | |
359 | bool QQmlPropertyCache::isAllowedInRevision(QQmlPropertyData *data) const |
360 | { |
361 | return (data->metaObjectOffset() == -1 && data->revision() == 0) || |
362 | (allowedRevisionCache[data->metaObjectOffset()] >= data->revision()); |
363 | } |
364 | |
365 | int QQmlPropertyCache::propertyCount() const |
366 | { |
367 | return propertyIndexCacheStart + propertyIndexCache.count(); |
368 | } |
369 | |
370 | int QQmlPropertyCache::propertyOffset() const |
371 | { |
372 | return propertyIndexCacheStart; |
373 | } |
374 | |
375 | int QQmlPropertyCache::methodCount() const |
376 | { |
377 | return methodIndexCacheStart + methodIndexCache.count(); |
378 | } |
379 | |
380 | int QQmlPropertyCache::methodOffset() const |
381 | { |
382 | return methodIndexCacheStart; |
383 | } |
384 | |
385 | int QQmlPropertyCache::signalCount() const |
386 | { |
387 | return signalHandlerIndexCacheStart + signalHandlerIndexCache.count(); |
388 | } |
389 | |
390 | int QQmlPropertyCache::signalOffset() const |
391 | { |
392 | return signalHandlerIndexCacheStart; |
393 | } |
394 | |
395 | int QQmlPropertyCache::qmlEnumCount() const |
396 | { |
397 | return enumCache.count(); |
398 | } |
399 | |
400 | bool QQmlPropertyCache::callJSFactoryMethod(QObject *object, void **args) const |
401 | { |
402 | if (_jsFactoryMethodIndex != -1) { |
403 | _metaObject->d.static_metacall(object, QMetaObject::InvokeMetaMethod, _jsFactoryMethodIndex, args); |
404 | return true; |
405 | } |
406 | if (_parent) |
407 | return _parent->callJSFactoryMethod(object, args); |
408 | return false; |
409 | } |
410 | |
411 | QT_END_NAMESPACE |
412 | |
413 | #endif // QQMLPROPERTYCACHE_P_H |
414 | |