1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QQMLPROPERTYCACHE_P_H
5#define QQMLPROPERTYCACHE_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <private/qlinkedstringhash_p.h>
19#include <private/qqmlenumdata_p.h>
20#include <private/qqmlenumvalue_p.h>
21#include <private/qqmlpropertydata_p.h>
22#include <private/qqmlrefcount_p.h>
23
24#include <QtCore/qvarlengtharray.h>
25#include <QtCore/qvector.h>
26#include <QtCore/qversionnumber.h>
27
28#include <limits>
29
30QT_BEGIN_NAMESPACE
31
32class QCryptographicHash;
33class QJSEngine;
34class QMetaObjectBuilder;
35class QQmlContextData;
36class QQmlPropertyCache;
37class QQmlPropertyCacheMethodArguments;
38class QQmlVMEMetaObject;
39
40class QQmlMetaObjectPointer
41{
42public:
43 Q_NODISCARD_CTOR QQmlMetaObjectPointer() = default;
44
45 Q_NODISCARD_CTOR QQmlMetaObjectPointer(const QMetaObject *staticMetaObject)
46 : d(quintptr(staticMetaObject))
47 {
48 Q_ASSERT((d.loadRelaxed() & Shared) == 0);
49 }
50
51 ~QQmlMetaObjectPointer()
52 {
53 const auto dd = d.loadAcquire();
54 if (dd & Shared)
55 reinterpret_cast<SharedHolder *>(dd ^ Shared)->release();
56 }
57
58private:
59 friend class QQmlPropertyCache;
60 Q_NODISCARD_CTOR QQmlMetaObjectPointer(const QQmlMetaObjectPointer &other)
61 : d(other.d.loadRelaxed())
62 {
63 // other has to survive until this ctor is done. So d cannot disappear before.
64 const auto od = other.d.loadRelaxed();
65 if (od & Shared)
66 reinterpret_cast<SharedHolder *>(od ^ Shared)->addref();
67 }
68
69 QQmlMetaObjectPointer(QQmlMetaObjectPointer &&other) = delete;
70 QQmlMetaObjectPointer &operator=(QQmlMetaObjectPointer &&other) = delete;
71 QQmlMetaObjectPointer &operator=(const QQmlMetaObjectPointer &other) = delete;
72
73public:
74 void setSharedOnce(QMetaObject *shared) const
75 {
76 SharedHolder *holder = new SharedHolder(shared);
77 if (!d.testAndSetRelease(expectedValue: 0, newValue: quintptr(holder) | Shared))
78 holder->release();
79 }
80
81 const QMetaObject *metaObject() const
82 {
83 const auto dd = d.loadAcquire();
84 if (dd & Shared)
85 return reinterpret_cast<SharedHolder *>(dd ^ Shared)->metaObject;
86 return reinterpret_cast<const QMetaObject *>(dd);
87 }
88
89 bool isShared() const
90 {
91 // This works because static metaobjects need to be set in the ctor and once a shared
92 // metaobject has been set, it cannot be removed anymore.
93 const auto dd = d.loadRelaxed();
94 return !dd || (dd & Shared);
95 }
96
97 bool isNull() const
98 {
99 return d.loadRelaxed() == 0;
100 }
101
102private:
103 enum Tag {
104 Static = 0,
105 Shared = 1
106 };
107
108 struct SharedHolder : public QQmlRefCounted<SharedHolder>
109 {
110 Q_DISABLE_COPY_MOVE(SharedHolder)
111 SharedHolder(QMetaObject *shared) : metaObject(shared) {}
112 ~SharedHolder() { free(ptr: metaObject); }
113 QMetaObject *metaObject;
114 };
115
116 mutable QBasicAtomicInteger<quintptr> d = 0;
117};
118
119class Q_QML_PRIVATE_EXPORT QQmlPropertyCache
120 : public QQmlRefCounted<QQmlPropertyCache>
121{
122public:
123 using Ptr = QQmlRefPointer<QQmlPropertyCache>;
124
125 struct ConstPtr : public QQmlRefPointer<const QQmlPropertyCache>
126 {
127 using QQmlRefPointer<const QQmlPropertyCache>::QQmlRefPointer;
128
129 ConstPtr(const Ptr &ptr) : ConstPtr(ptr.data(), AddRef) {}
130 ConstPtr(Ptr &&ptr) : ConstPtr(ptr.take(), Adopt) {}
131 ConstPtr &operator=(const Ptr &ptr) { return operator=(ConstPtr(ptr)); }
132 ConstPtr &operator=(Ptr &&ptr) { return operator=(ConstPtr(std::move(ptr))); }
133 };
134
135 static Ptr createStandalone(
136 const QMetaObject *, QTypeRevision metaObjectRevision = QTypeRevision::zero());
137
138 QQmlPropertyCache() = default;
139 ~QQmlPropertyCache() override;
140
141 void update(const QMetaObject *);
142 void invalidate(const QMetaObject *);
143
144 QQmlPropertyCache::Ptr copy() const;
145
146 QQmlPropertyCache::Ptr copyAndAppend(
147 const QMetaObject *, QTypeRevision typeVersion,
148 QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(),
149 QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(),
150 QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags()) const;
151
152 QQmlPropertyCache::Ptr copyAndReserve(
153 int propertyCount, int methodCount, int signalCount, int enumCount) const;
154 void appendProperty(const QString &, QQmlPropertyData::Flags flags, int coreIndex,
155 QMetaType propType, QTypeRevision revision, int notifyIndex);
156 void appendSignal(const QString &, QQmlPropertyData::Flags, int coreIndex,
157 const QMetaType *types = nullptr,
158 const QList<QByteArray> &names = QList<QByteArray>());
159 void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex,
160 QMetaType returnType, const QList<QByteArray> &names,
161 const QVector<QMetaType> &parameterTypes);
162 void appendEnum(const QString &, const QVector<QQmlEnumValue> &);
163
164 const QMetaObject *metaObject() const;
165 const QMetaObject *createMetaObject() const;
166 const QMetaObject *firstCppMetaObject() const;
167
168 template<typename K>
169 const QQmlPropertyData *property(const K &key, QObject *object,
170 const QQmlRefPointer<QQmlContextData> &context) const
171 {
172 return findProperty(stringCache.find(key), object, context);
173 }
174
175 const QQmlPropertyData *property(int) const;
176 const QQmlPropertyData *maybeUnresolvedProperty(int) const;
177 const QQmlPropertyData *method(int) const;
178 const QQmlPropertyData *signal(int index) const;
179 QQmlEnumData *qmlEnum(int) const;
180 int methodIndexToSignalIndex(int) const;
181
182 QString defaultPropertyName() const;
183 const QQmlPropertyData *defaultProperty() const;
184
185 // Return a reference here so that we don't have to addref/release all the time
186 inline const QQmlPropertyCache::ConstPtr &parent() const;
187
188 // is used by the Qml Designer
189 void setParent(QQmlPropertyCache::ConstPtr newParent);
190
191 inline const QQmlPropertyData *overrideData(const QQmlPropertyData *) const;
192 inline bool isAllowedInRevision(const QQmlPropertyData *) const;
193
194 static const QQmlPropertyData *property(
195 QObject *, QStringView, const QQmlRefPointer<QQmlContextData> &,
196 QQmlPropertyData *);
197 static const QQmlPropertyData *property(QObject *, const QLatin1String &, const QQmlRefPointer<QQmlContextData> &,
198 QQmlPropertyData *);
199 static const QQmlPropertyData *property(QObject *, const QV4::String *, const QQmlRefPointer<QQmlContextData> &,
200 QQmlPropertyData *);
201
202 //see QMetaObjectPrivate::originalClone
203 int originalClone(int index) const;
204 static int originalClone(const QObject *, int index);
205
206 QList<QByteArray> signalParameterNames(int index) const;
207 static QString signalParameterStringForJS(QV4::ExecutionEngine *engine, const QList<QByteArray> &parameterNameList, QString *errorString = nullptr);
208
209 const char *className() const;
210
211 inline int propertyCount() const;
212 inline int propertyOffset() const;
213 inline int methodCount() const;
214 inline int methodOffset() const;
215 inline int signalCount() const;
216 inline int signalOffset() const;
217 inline int qmlEnumCount() const;
218
219 void toMetaObjectBuilder(QMetaObjectBuilder &) const;
220
221 inline bool callJSFactoryMethod(QObject *object, void **args) const;
222
223 static bool determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, int *stringCount);
224 static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo);
225
226 QByteArray checksum(QHash<quintptr, QByteArray> *checksums, bool *ok) const;
227
228 QTypeRevision allowedRevision(int index) const { return allowedRevisionCache[index]; }
229 void setAllowedRevision(int index, QTypeRevision allowed) { allowedRevisionCache[index] = allowed; }
230
231private:
232 friend class QQmlEnginePrivate;
233 friend class QQmlCompiler;
234 template <typename T> friend class QQmlPropertyCacheCreator;
235 template <typename T> friend class QQmlPropertyCacheAliasCreator;
236 template <typename T> friend class QQmlComponentAndAliasResolver;
237 friend class QQmlMetaObject;
238
239 QQmlPropertyCache(const QQmlMetaObjectPointer &metaObject) : _metaObject(metaObject) {}
240
241 inline QQmlPropertyCache::Ptr copy(const QQmlMetaObjectPointer &mo, int reserve) const;
242
243 void append(const QMetaObject *, QTypeRevision typeVersion,
244 QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(),
245 QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(),
246 QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags());
247
248 QQmlPropertyCacheMethodArguments *createArgumentsObject(int count, const QList<QByteArray> &names);
249
250 typedef QVector<QQmlPropertyData> IndexCache;
251 typedef QLinkedStringMultiHash<QPair<int, QQmlPropertyData *> > StringCache;
252 typedef QVector<QTypeRevision> AllowedRevisionCache;
253
254 const QQmlPropertyData *findProperty(StringCache::ConstIterator it, QObject *,
255 const QQmlRefPointer<QQmlContextData> &) const;
256 const QQmlPropertyData *findProperty(StringCache::ConstIterator it, const QQmlVMEMetaObject *,
257 const QQmlRefPointer<QQmlContextData> &) const;
258
259 template<typename K>
260 QQmlPropertyData *findNamedProperty(const K &key) const
261 {
262 StringCache::mapped_type *it = stringCache.value(key);
263 return it ? it->second : 0;
264 }
265
266 template<typename K>
267 void setNamedProperty(const K &key, int index, QQmlPropertyData *data)
268 {
269 stringCache.insert(key, qMakePair(value1&: index, value2&: data));
270 }
271
272private:
273 enum OverrideResult { NoOverride, InvalidOverride, ValidOverride };
274
275 template<typename String>
276 OverrideResult handleOverride(const String &name, QQmlPropertyData *data, QQmlPropertyData *old)
277 {
278 if (!old)
279 return NoOverride;
280
281 if (data->markAsOverrideOf(predecessor: old))
282 return ValidOverride;
283
284 qWarning("Final member %s is overridden in class %s. The override won't be used.",
285 qPrintable(name), className());
286 return InvalidOverride;
287 }
288
289 template<typename String>
290 OverrideResult handleOverride(const String &name, QQmlPropertyData *data)
291 {
292 return handleOverride(name, data, findNamedProperty(name));
293 }
294
295 int propertyIndexCacheStart = 0; // placed here to avoid gap between QQmlRefCount and _parent
296 QQmlPropertyCache::ConstPtr _parent;
297
298 IndexCache propertyIndexCache;
299 IndexCache methodIndexCache;
300 IndexCache signalHandlerIndexCache;
301 StringCache stringCache;
302 AllowedRevisionCache allowedRevisionCache;
303 QVector<QQmlEnumData> enumCache;
304
305 QQmlMetaObjectPointer _metaObject;
306 QByteArray _dynamicClassName;
307 QByteArray _dynamicStringData;
308 QByteArray _listPropertyAssignBehavior;
309 QString _defaultPropertyName;
310 QQmlPropertyCacheMethodArguments *argumentsCache = nullptr;
311 int methodIndexCacheStart = 0;
312 int signalHandlerIndexCacheStart = 0;
313 int _jsFactoryMethodIndex = -1;
314};
315
316// Returns this property cache's metaObject. May be null if it hasn't been created yet.
317inline const QMetaObject *QQmlPropertyCache::metaObject() const
318{
319 return _metaObject.metaObject();
320}
321
322// Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by
323// QML
324inline const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const
325{
326 const QQmlPropertyCache *p = this;
327 while (p->_metaObject.isShared())
328 p = p->parent().data();
329 return p->_metaObject.metaObject();
330}
331
332inline const QQmlPropertyData *QQmlPropertyCache::property(int index) const
333{
334 if (index < 0 || index >= propertyCount())
335 return nullptr;
336
337 if (index < propertyIndexCacheStart)
338 return _parent->property(index);
339
340 return &propertyIndexCache.at(i: index - propertyIndexCacheStart);
341}
342
343inline const QQmlPropertyData *QQmlPropertyCache::method(int index) const
344{
345 if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.size()))
346 return nullptr;
347
348 if (index < methodIndexCacheStart)
349 return _parent->method(index);
350
351 return const_cast<const QQmlPropertyData *>(&methodIndexCache.at(i: index - methodIndexCacheStart));
352}
353
354/*! \internal
355 \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
356 This is different from QMetaMethod::methodIndex().
357*/
358inline const QQmlPropertyData *QQmlPropertyCache::signal(int index) const
359{
360 if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.size()))
361 return nullptr;
362
363 if (index < signalHandlerIndexCacheStart)
364 return _parent->signal(index);
365
366 const QQmlPropertyData *rv = const_cast<const QQmlPropertyData *>(&methodIndexCache.at(i: index - signalHandlerIndexCacheStart));
367 Q_ASSERT(rv->isSignal() || rv->coreIndex() == -1);
368 return rv;
369}
370
371inline QQmlEnumData *QQmlPropertyCache::qmlEnum(int index) const
372{
373 if (index < 0 || index >= enumCache.size())
374 return nullptr;
375
376 return const_cast<QQmlEnumData *>(&enumCache.at(i: index));
377}
378
379inline int QQmlPropertyCache::methodIndexToSignalIndex(int index) const
380{
381 if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.size()))
382 return index;
383
384 if (index < methodIndexCacheStart)
385 return _parent->methodIndexToSignalIndex(index);
386
387 return index - methodIndexCacheStart + signalHandlerIndexCacheStart;
388}
389
390// Returns the name of the default property for this cache
391inline QString QQmlPropertyCache::defaultPropertyName() const
392{
393 return _defaultPropertyName;
394}
395
396inline const QQmlPropertyCache::ConstPtr &QQmlPropertyCache::parent() const
397{
398 return _parent;
399}
400
401const QQmlPropertyData *
402QQmlPropertyCache::overrideData(const QQmlPropertyData *data) const
403{
404 if (!data->hasOverride())
405 return nullptr;
406
407 if (data->overrideIndexIsProperty())
408 return property(index: data->overrideIndex());
409 else
410 return method(index: data->overrideIndex());
411}
412
413bool QQmlPropertyCache::isAllowedInRevision(const QQmlPropertyData *data) const
414{
415 const QTypeRevision requested = data->revision();
416 const int offset = data->metaObjectOffset();
417 if (offset == -1 && requested == QTypeRevision::zero())
418 return true;
419
420 Q_ASSERT(offset >= 0);
421 Q_ASSERT(offset < allowedRevisionCache.size());
422 const QTypeRevision allowed = allowedRevisionCache[offset];
423
424 if (requested.hasMajorVersion()) {
425 if (requested.majorVersion() > allowed.majorVersion())
426 return false;
427 if (requested.majorVersion() < allowed.majorVersion())
428 return true;
429 }
430
431 return !requested.hasMinorVersion() || requested.minorVersion() <= allowed.minorVersion();
432}
433
434int QQmlPropertyCache::propertyCount() const
435{
436 return propertyIndexCacheStart + int(propertyIndexCache.size());
437}
438
439int QQmlPropertyCache::propertyOffset() const
440{
441 return propertyIndexCacheStart;
442}
443
444int QQmlPropertyCache::methodCount() const
445{
446 return methodIndexCacheStart + int(methodIndexCache.size());
447}
448
449int QQmlPropertyCache::methodOffset() const
450{
451 return methodIndexCacheStart;
452}
453
454int QQmlPropertyCache::signalCount() const
455{
456 return signalHandlerIndexCacheStart + int(signalHandlerIndexCache.size());
457}
458
459int QQmlPropertyCache::signalOffset() const
460{
461 return signalHandlerIndexCacheStart;
462}
463
464int QQmlPropertyCache::qmlEnumCount() const
465{
466 return int(enumCache.size());
467}
468
469bool QQmlPropertyCache::callJSFactoryMethod(QObject *object, void **args) const
470{
471 if (_jsFactoryMethodIndex != -1) {
472 if (const QMetaObject *mo = _metaObject.metaObject()) {
473 mo->d.static_metacall(object, QMetaObject::InvokeMetaMethod,
474 _jsFactoryMethodIndex, args);
475 return true;
476 }
477 return false;
478 }
479 if (_parent)
480 return _parent->callJSFactoryMethod(object, args);
481 return false;
482}
483
484QT_END_NAMESPACE
485
486#endif // QQMLPROPERTYCACHE_P_H
487

source code of qtdeclarative/src/qml/qml/qqmlpropertycache_p.h