Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlpropertycache_p.h
Go to the documentation of this file.
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#include <private/qexpected_p.h>
24
25#include <QtCore/qvarlengtharray.h>
26#include <QtCore/qvector.h>
27#include <QtCore/qversionnumber.h>
28#include <QtCore/qxpfunctional.h>
29#include <QtCore/qloggingcategory.h>
30
31#include <limits>
32
33class tst_qqmlpropertycache;
34
35QT_BEGIN_NAMESPACE
36
37class QCryptographicHash;
38class QJSEngine;
39class QMetaObjectBuilder;
40class QQmlContextData;
41class QQmlPropertyCache;
43class QQmlVMEMetaObject;
44
46{
47public:
49
55
57 {
58 const auto dd = d.loadAcquire();
59 if (dd & Shared)
60 reinterpret_cast<SharedHolder *>(dd ^ Shared)->release();
61 }
62
63private:
64 friend class QQmlPropertyCache;
66 : d(other.d.loadRelaxed())
67 {
68 // other has to survive until this ctor is done. So d cannot disappear before.
69 const auto od = other.d.loadRelaxed();
70 if (od & Shared)
71 reinterpret_cast<SharedHolder *>(od ^ Shared)->addref();
72 }
73
74 QQmlMetaObjectPointer(QQmlMetaObjectPointer &&other) = delete;
75 QQmlMetaObjectPointer &operator=(QQmlMetaObjectPointer &&other) = delete;
76 QQmlMetaObjectPointer &operator=(const QQmlMetaObjectPointer &other) = delete;
77
78public:
79 void setSharedOnce(QMetaObject *shared) const
80 {
81 SharedHolder *holder = new SharedHolder(shared);
82 if (!d.testAndSetRelease(0, quintptr(holder) | Shared))
83 holder->release();
84 }
85
86 const QMetaObject *metaObject() const
87 {
88 const auto dd = d.loadAcquire();
89 if (dd & Shared)
90 return reinterpret_cast<SharedHolder *>(dd ^ Shared)->metaObject;
91 return reinterpret_cast<const QMetaObject *>(dd);
92 }
93
94 bool isShared() const
95 {
96 // This works because static metaobjects need to be set in the ctor and once a shared
97 // metaobject has been set, it cannot be removed anymore.
98 const auto dd = d.loadRelaxed();
99 return !dd || (dd & Shared);
100 }
101
102 bool isNull() const
103 {
104 return d.loadRelaxed() == 0;
105 }
106
107private:
108 enum Tag {
109 Static = 0,
110 Shared = 1
111 };
112
113 struct SharedHolder final : public QQmlRefCounted<SharedHolder>
114 {
115 Q_DISABLE_COPY_MOVE(SharedHolder)
116 SharedHolder(QMetaObject *shared) : metaObject(shared) {}
117 ~SharedHolder() { free(metaObject); }
118 QMetaObject *metaObject;
119 };
120
121 mutable QBasicAtomicInteger<quintptr> d = 0;
122};
123
124// TODO should be moved somewhere else?
125namespace OverrideSemantics {
126enum class Status : uint8_t {
127 Valid = 0,
128 NoOverride, // Derived { (virtual|final) property var a;} Base {}
129
130 MissingBase, // Derived { override property var a;} Base {}
131 OverridingFinal, // Derived { * property var a;} Base {final property var a;}
132 OverridingNonVirtual, // Derived { (virtual|final) property var a;} Base {property var a;}
133 OverridingNonVirtualError, // Derived { override property var a;} Base {property var a;}
134 MissingOverrideOrFinalSpecifier, // Derived { (virtual) property var a;} Base { virtual property var a;}
135
136 InvokabilityMismatch, // property overrides method(function, slot, invokable) or vice-versa
137
139};
140
141inline bool isValidOverride(Status res)
142{
143 return res == Status::NoOverride
144 || res == Status::Valid
145 // MissingOverrideOrFinalSpecifier, OverridingNonVirtual and InvokabilityMismatch
146 // are currently considered valid to preserve backwards compatibility.
147 // It will become invalid in Qt7
150}
151
152enum class CheckMode : uint8_t {
153 Minimal = 0, // pre virtual and override keywords
155};
156
157[[nodiscard]] Q_AUTOTEST_EXPORT Status handleOverride(QQmlPropertyData &overridingProperty,
160
163
164} // namespace OverrideSemantics
165
166Q_DECLARE_LOGGING_CATEGORY(qqmlPropertyCacheAppend)
167
168/*
169 * QQmlPropertyCache has the following structure:
170 *
171 * It maintains a non-owning map (StringCache) that associates each property name
172 * with a pointer to a QQmlPropertyData object. Each pointer refers to an element
173 * stored in one of the owning containers — for example, PropertyIndexCache or
174 * MethodsIndexCache.
175 *
176 * As a result, if an owning container such as PropertyIndexCache is resized,
177 * the entries in StringCache become invalid.
178 *
179 * To protect users of this class from such undefined behavior, most modifying
180 * methods are kept private. A few trusted classes, such as QQmlPropertyCacheCreator,
181 * are declared as friends. As the name suggests, QQmlPropertyCacheCreator is responsible
182 * for constructing and populating a QQmlPropertyCache instance in one controlled place,
183 * ensuring that all indices and data remain coherent.
184 *
185 * Because the internal logic of this class is non-trivial, the unit test
186 * (tst_qqmlpropertycache) is also declared as a friend to support proper testing
187 * and maintainability.
188 */
189class Q_QML_EXPORT QQmlPropertyCache final : public QQmlRefCounted<QQmlPropertyCache>
190{
191public:
192 using Ptr = QQmlRefPointer<QQmlPropertyCache>;
193
194 struct ConstPtr : public QQmlRefPointer<const QQmlPropertyCache>
195 {
196 using QQmlRefPointer<const QQmlPropertyCache>::QQmlRefPointer;
197
198 ConstPtr(const Ptr &ptr) : ConstPtr(ptr.data(), AddRef) {}
199 ConstPtr(Ptr &&ptr) : ConstPtr(ptr.take(), Adopt) {}
200 ConstPtr &operator=(const Ptr &ptr) { return operator=(ConstPtr(ptr)); }
201 ConstPtr &operator=(Ptr &&ptr) { return operator=(ConstPtr(std::move(ptr))); }
202 };
203
204 static Ptr createStandalone(
205 const QMetaObject *, QTypeRevision metaObjectRevision = QTypeRevision::zero());
206
207 QQmlPropertyCache(
208 OverrideSemantics::HandlerRef handleOverride = OverrideSemantics::handleOverride)
209 : _handleOverride(handleOverride) { };
210 ~QQmlPropertyCache();
211
212 void update(const QMetaObject *);
213 void invalidate(const QMetaObject *);
214
215 QQmlPropertyCache::Ptr copy() const;
216
217 QQmlPropertyCache::Ptr copyAndAppend(
218 const QMetaObject *, QTypeRevision typeVersion,
219 QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(),
220 QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(),
221 QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags()) const;
222
223 QQmlPropertyCache::Ptr copyAndReserve(
224 int propertyCount, int methodCount, int signalCount, int enumCount) const;
225
226 const QMetaObject *metaObject() const;
227 const QMetaObject *createMetaObject() const;
228 const QMetaObject *firstCppMetaObject() const;
229
230 template<typename K>
231 const QQmlPropertyData *property(const K &key, QObject *object,
232 const QQmlRefPointer<QQmlContextData> &context) const
233 {
234 return findProperty(stringCache.find(key), object, context);
235 }
236
237 const QQmlPropertyData *property(int) const;
238 const QQmlPropertyData *maybeUnresolvedProperty(int) const;
239 const QQmlPropertyData *method(int) const;
240 const QQmlPropertyData *signal(int index) const;
241 QQmlEnumData *qmlEnum(int) const;
242 int methodIndexToSignalIndex(int) const;
243
244 QString defaultPropertyName() const;
245 const QQmlPropertyData *defaultProperty() const;
246
247 // Return a reference here so that we don't have to addref/release all the time
248 inline const QQmlPropertyCache::ConstPtr &parent() const;
249
250 // is used by the Qml Designer
251 void setParent(QQmlPropertyCache::ConstPtr newParent);
252
253 inline const QQmlPropertyData *overrideData(const QQmlPropertyData *) const;
254 inline bool isAllowedInRevision(const QQmlPropertyData *) const;
255
256 static const QQmlPropertyData *property(
257 QObject *, QStringView, const QQmlRefPointer<QQmlContextData> &,
258 QQmlPropertyData *);
259 static const QQmlPropertyData *property(QObject *, const QLatin1String &, const QQmlRefPointer<QQmlContextData> &,
260 QQmlPropertyData *);
261 static const QQmlPropertyData *property(QObject *, const QV4::String *, const QQmlRefPointer<QQmlContextData> &,
262 QQmlPropertyData *);
263
264 //see QMetaObjectPrivate::originalClone
265 int originalClone(int index) const;
266 static int originalClone(const QObject *, int index);
267
268 QList<QByteArray> signalParameterNames(int index) const;
269 static QString signalParameterStringForJS(
270 const QList<QByteArray> &parameterNameList, QString *errorString = nullptr);
271
272 const char *className() const;
273
274 inline int propertyCount() const;
275 inline int ownPropertyCount() const { return int(propertyIndexCache.count()); }
276 inline int propertyOffset() const;
277 inline int methodCount() const;
278 inline int ownMethodCount() const { return int(methodIndexCache.count()); }
279 inline int methodOffset() const;
280 inline int signalCount() const;
281 inline int ownSignalCount() const { return int(signalHandlerIndexCache.count()); }
282 inline int signalOffset() const;
283 inline int qmlEnumCount() const;
284
285 void toMetaObjectBuilder(QMetaObjectBuilder &) const;
286
287 inline bool callJSFactoryMethod(QObject *object, void **args) const;
288
289 static bool determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, int *stringCount);
290 static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo);
291
292 QByteArray checksum(QHash<quintptr, QByteArray> *checksums, bool *ok) const;
293
294 QTypeRevision allowedRevision(int index) const { return allowedRevisionCache[index]; }
295 void setAllowedRevision(int index, QTypeRevision allowed) { allowedRevisionCache[index] = allowed; }
296
297private:
298 friend class QQmlEnginePrivate;
299 friend class QQmlCompiler;
300 template <typename T> friend class QQmlPropertyCacheCreator;
301 template <typename T> friend class QQmlPropertyCacheAliasCreator;
302 template <typename T> friend class QQmlComponentAndAliasResolver;
303 friend class QQmlMetaObject;
304 friend class ::tst_qqmlpropertycache;
305
306 QQmlPropertyCache(
307 const QQmlMetaObjectPointer &metaObject,
308 OverrideSemantics::HandlerRef handleOverride = OverrideSemantics::handleOverride)
309 : _handleOverride(handleOverride), _metaObject(metaObject)
310 {
311 }
312
313 inline QQmlPropertyCache::Ptr copy(const QQmlMetaObjectPointer &mo, int reserve) const;
314
315 void append(const QMetaObject *, QTypeRevision typeVersion,
316 QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(),
317 QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(),
318 QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags());
319
320 // TODO incorporate warning from maybeLog
321 using Error = OverrideSemantics::Status;
322 using AppendResult = q23::expected<void, Error>;
323
324 // TODO QTBUG-141728
325 // Always adds a property to the propertyIndex even if override semantics are invalid, not
326 // accessible by name though
327 AppendResult appendPropertyAttr(const QString &name, QQmlPropertyData &&data);
328 AppendResult appendAlias(const QString &, QQmlPropertyData::Flags flags, int coreIndex,
329 QMetaType propType, QTypeRevision version, int notifyIndex,
330 int encodedTargetIndex);
331 void appendSignal(const QString &, QQmlPropertyData::Flags, int coreIndex,
332 const QMetaType *types = nullptr,
333 const QList<QByteArray> &names = QList<QByteArray>());
334 void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex,
335 QMetaType returnType, const QList<QByteArray> &names,
336 const QList<QMetaType> &parameterTypes);
337 void appendEnum(const QString &, const QList<QQmlEnumValue> &);
338
339 QQmlPropertyCacheMethodArguments *createArgumentsObject(int count,
340 const QList<QByteArray> &names);
341
342 typedef QList<QQmlPropertyData> IndexCache;
343 typedef QLinkedStringMultiHash<std::pair<int, QQmlPropertyData *> > StringCache;
344 typedef QList<QTypeRevision> AllowedRevisionCache;
345
346 const QQmlPropertyData *findProperty(StringCache::ConstIterator it, QObject *,
347 const QQmlRefPointer<QQmlContextData> &) const;
348 const QQmlPropertyData *findProperty(StringCache::ConstIterator it, const QQmlVMEMetaObject *,
349 const QQmlRefPointer<QQmlContextData> &) const;
350
351 template<typename K>
352 QQmlPropertyData *findNamedProperty(const K &key) const
353 {
354 StringCache::mapped_type *it = stringCache.value(key);
355 return it ? it->second : 0;
356 }
357
358 template<typename K>
359 void setNamedProperty(const K &key, int index, QQmlPropertyData *data)
360 {
361 stringCache.insert(key, std::make_pair(index, data));
362 }
363
364 // Conditionally logs diagnostics for override semantics.
365 //
366 // Important: "Valid" override semantics do not imply "no warning".
367 // Some override situations are allowed by the language rules but are still
368 // diagnosed to help users avoid surprising or unintended behavior.
369 //
370 // Does not construct diagnostic messages when no logging is required
371 // to compute qPrintable(name) and className() only when needed.
372 template <typename String>
373 void maybeLog(OverrideSemantics::Status status, const String &name) const
374 {
375 switch (status) {
376 case OverrideSemantics::Status::OverridingFinal: {
377 qCWarning(qqmlPropertyCacheAppend).noquote()
378 << QStringLiteral("Final member %1 is overridden in class %2. The "
379 "override won't be used.")
380 .arg(qPrintable(name), className());
381 return;
382 }
383 case OverrideSemantics::Status::MissingBase: {
384 qCWarning(qqmlPropertyCacheAppend).noquote()
385 << QStringLiteral("Member %1 of the object %2 does not override anything."
386 " Consider removing \"override\". ")
387 .arg(qPrintable(name), className());
388 return;
389 }
390 case OverrideSemantics::Status::OverridingNonVirtual:
391 case OverrideSemantics::Status::OverridingNonVirtualError: {
392 qCWarning(qqmlPropertyCacheAppend).noquote()
393 << QStringLiteral("Member %1 of the object %2 overrides a non-virtual member. "
394 "Consider renaming it or mark it virtual in the base object")
395 .arg(qPrintable(name), className());
396 return;
397 }
398 case OverrideSemantics::Status::MissingOverrideOrFinalSpecifier: {
399 qCWarning(qqmlPropertyCacheAppend).noquote()
400 << QStringLiteral(
401 "Member %s of the object %s overrides a member of the base object. "
402 "Consider renaming it or adding final or override specifier")
403 .arg(qPrintable(name), className());
404 return;
405 }
406 // TODO QTBUG-98320, when override is cleaned up for methods, we need a warning for InvokabilityMismatch
407 default:
408 return;
409 }
410 }
411
412private:
413 OverrideSemantics::HandlerRef _handleOverride;
414
415 int propertyIndexCacheStart = 0; // placed here to avoid gap between QQmlRefCount and _parent
416 QQmlPropertyCache::ConstPtr _parent;
417
418 IndexCache propertyIndexCache;
419 IndexCache methodIndexCache;
420 IndexCache signalHandlerIndexCache;
421 StringCache stringCache;
422 AllowedRevisionCache allowedRevisionCache;
423 QList<QQmlEnumData> enumCache;
424
425 QQmlMetaObjectPointer _metaObject;
426 QByteArray _dynamicClassName;
427 QByteArray _dynamicStringData;
428 QByteArray _listPropertyAssignBehavior;
429 QString _defaultPropertyName;
430 QQmlPropertyCacheMethodArguments *argumentsCache = nullptr;
431 int methodIndexCacheStart = 0;
432 int signalHandlerIndexCacheStart = 0;
433 int _jsFactoryMethodIndex = -1;
434};
435
436// Returns this property cache's metaObject. May be null if it hasn't been created yet.
437inline const QMetaObject *QQmlPropertyCache::metaObject() const
438{
439 return _metaObject.metaObject();
440}
441
442// Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by
443// QML
444inline const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const
445{
446 const QQmlPropertyCache *p = this;
447 while (p->_metaObject.isShared())
448 p = p->parent().data();
449 return p->_metaObject.metaObject();
450}
451
452inline const QQmlPropertyData *QQmlPropertyCache::property(int index) const
453{
454 if (index < 0 || index >= propertyCount())
455 return nullptr;
456
457 if (index < propertyIndexCacheStart)
458 return _parent->property(index);
459
460 return &propertyIndexCache.at(index - propertyIndexCacheStart);
461}
462
463inline const QQmlPropertyData *QQmlPropertyCache::method(int index) const
464{
465 if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.size()))
466 return nullptr;
467
468 if (index < methodIndexCacheStart)
469 return _parent->method(index);
470
471 return const_cast<const QQmlPropertyData *>(&methodIndexCache.at(index - methodIndexCacheStart));
472}
473
474/*! \internal
475 \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
476 This is different from QMetaMethod::methodIndex().
477*/
478inline const QQmlPropertyData *QQmlPropertyCache::signal(int index) const
479{
480 if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.size()))
481 return nullptr;
482
483 if (index < signalHandlerIndexCacheStart)
484 return _parent->signal(index);
485
486 const QQmlPropertyData *rv = const_cast<const QQmlPropertyData *>(&methodIndexCache.at(index - signalHandlerIndexCacheStart));
487 Q_ASSERT(rv->isSignal() || rv->coreIndex() == -1);
488 return rv;
489}
490
491inline QQmlEnumData *QQmlPropertyCache::qmlEnum(int index) const
492{
493 if (index < 0 || index >= enumCache.size())
494 return nullptr;
495
496 return const_cast<QQmlEnumData *>(&enumCache.at(index));
497}
498
499inline int QQmlPropertyCache::methodIndexToSignalIndex(int index) const
500{
501 if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.size()))
502 return index;
503
504 if (index < methodIndexCacheStart)
505 return _parent->methodIndexToSignalIndex(index);
506
507 return index - methodIndexCacheStart + signalHandlerIndexCacheStart;
508}
509
510// Returns the name of the default property for this cache
511inline QString QQmlPropertyCache::defaultPropertyName() const
512{
513 return _defaultPropertyName;
514}
515
516inline const QQmlPropertyCache::ConstPtr &QQmlPropertyCache::parent() const
517{
518 return _parent;
519}
520
521const QQmlPropertyData *
522QQmlPropertyCache::overrideData(const QQmlPropertyData *data) const
523{
524 if (!data->hasOverride())
525 return nullptr;
526
527 if (data->overrideIndexIsProperty())
528 return property(data->overrideIndex());
529 else
530 return method(data->overrideIndex());
531}
532
533bool QQmlPropertyCache::isAllowedInRevision(const QQmlPropertyData *data) const
534{
535 const QTypeRevision requested = data->revision();
536 const int offset = data->metaObjectOffset();
537 if (offset == -1 && requested == QTypeRevision::zero())
538 return true;
539
540 Q_ASSERT(offset >= 0);
541 Q_ASSERT(offset < allowedRevisionCache.size());
542 const QTypeRevision allowed = allowedRevisionCache[offset];
543
544 if (requested.hasMajorVersion()) {
545 if (requested.majorVersion() > allowed.majorVersion())
546 return false;
547 if (requested.majorVersion() < allowed.majorVersion())
548 return true;
549 }
550
551 return !requested.hasMinorVersion() || requested.minorVersion() <= allowed.minorVersion();
552}
553
554int QQmlPropertyCache::propertyCount() const
555{
556 return propertyIndexCacheStart + int(propertyIndexCache.size());
557}
558
559int QQmlPropertyCache::propertyOffset() const
560{
561 return propertyIndexCacheStart;
562}
563
564int QQmlPropertyCache::methodCount() const
565{
566 return methodIndexCacheStart + int(methodIndexCache.size());
567}
568
569int QQmlPropertyCache::methodOffset() const
570{
571 return methodIndexCacheStart;
572}
573
574int QQmlPropertyCache::signalCount() const
575{
576 return signalHandlerIndexCacheStart + int(signalHandlerIndexCache.size());
577}
578
579int QQmlPropertyCache::signalOffset() const
580{
581 return signalHandlerIndexCacheStart;
582}
583
584int QQmlPropertyCache::qmlEnumCount() const
585{
586 return int(enumCache.size());
587}
588
589bool QQmlPropertyCache::callJSFactoryMethod(QObject *object, void **args) const
590{
591 if (_jsFactoryMethodIndex != -1) {
592 if (const QMetaObject *mo = _metaObject.metaObject()) {
593 mo->d.static_metacall(object, QMetaObject::InvokeMetaMethod,
594 _jsFactoryMethodIndex, args);
595 return true;
596 }
597 return false;
598 }
599 if (_parent)
600 return _parent->callJSFactoryMethod(object, args);
601 return false;
602}
603
604QT_END_NAMESPACE
605
606#endif // QQMLPROPERTYCACHE_P_H
friend class QJSEngine
const QMetaObject * metaObject() const
void setSharedOnce(QMetaObject *shared) const
static Status checkMinimal(const QQmlPropertyData *const existingProperty)
static Status check(const QQmlPropertyData &overridingProperty, const QQmlPropertyData *const existingProperty, CheckMode mode)
static bool hasInvalidModifierCombintation(const QQmlPropertyData &overridingProperty)
static Status checkFull(const QQmlPropertyData &overridingProperty, const QQmlPropertyData *const existingProperty)
Status handleOverride(QQmlPropertyData &overridingProperty, QQmlPropertyData *existingProperty, CheckMode mode)
bool isValidOverride(Status res)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
const QQmlPropertyData * qQmlPropertyCacheProperty(QObject *obj, T name, const QQmlRefPointer< QQmlContextData > &context, QQmlPropertyData *local)
static const QByteArray stringData(const QMetaObject *mo, int index)
static QQmlPropertyData qQmlPropertyCacheCreate(const QMetaObject *metaObject, const T &propertyName)
#define Q_INT16_MAX
static const char * qQmlPropertyCacheToString(QLatin1String string)
static QByteArray qQmlPropertyCacheToString(const QV4::String *string)
static std::pair< bool, int > deriveEncodingAndLength(const char *str)
static QHashedString signalNameToHandlerName(const QHashedString &methodName)
static int metaObjectSignalCount(const QMetaObject *metaObject)