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.cpp
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
5
6#include <private/qqmlengine_p.h>
7#include <private/qqmlbinding_p.h>
8#include <private/qqmlvmemetaobject_p.h>
9
10#include <private/qmetaobject_p.h>
11#include <private/qmetaobjectbuilder_p.h>
12#include <private/qqmlpropertycachemethodarguments_p.h>
13#include <private/qqmlsignalnames_p.h>
14
15#include <private/qv4codegen_p.h>
16#include <private/qv4value_p.h>
17
18#include <QtCore/qdebug.h>
19#include <QtCore/QCryptographicHash>
20#include <QtCore/private/qtools_p.h>
21
22#include <limits.h>
23#include <algorithm>
24
25#ifdef Q_CC_MSVC
26// nonstandard extension used : zero-sized array in struct/union.
27# pragma warning( disable : 4200 )
28#endif
29
30QT_BEGIN_NAMESPACE
31
32#define Q_INT16_MAX 32767
33
35namespace detail {
36
37static inline bool hasInvalidModifierCombintation(const QQmlPropertyData &overridingProperty)
38{
39 return (overridingProperty.isVirtual() && overridingProperty.isFinal())
40 || (overridingProperty.doesOverride() && overridingProperty.isFinal())
41 || (overridingProperty.isVirtual() && overridingProperty.doesOverride());
42}
43
44/*
45 * Performs minimal validation of property override semantics.
46 *
47 * This function checks whether an existing property can be overridden.
48 * It distinguishes between the following cases:
49 * - No base property exists → Status::NoOverride
50 * - Base property is marked final → Status::OverridingFinal
51 * - Otherwise → Status::Valid
52 *
53 * The minimal check is used in contexts where only basic inheritance
54 * constraints (existence and finality) must be verified.
55 */
56static inline Status checkMinimal(const QQmlPropertyData *const existingProperty)
57{
58 if (!existingProperty)
59 return Status::NoOverride;
60
61 if (existingProperty->isFinal()) {
63 }
64
65 return Status::Valid;
66}
67
68/*
69 * Performs full validation of property override semantics.
70 *
71 * This function enforces the full set of rules for `virtual`, `override`,
72 * and `final` keyword combinations when resolving property overrides.
73 * It verifies:
74 *
75 * - If `override` is specified but no base property exists,
76 * the override is invalid (Status::MissingBase).
77 *
78 * - If `override` is NOT specified and no base property exists,
79 * then there is no override (Status::NoOverride).
80 *
81 * - If the base property is final, overriding is not allowed
82 * (Status::OverridingFinal).
83 *
84 * - If the base property is invokable and overriding is not (and vice-versa),
85 * override is invalid (Status::InvokabilityMismatch).
86 *
87 * - If the base property is not virtual, but 'override' is present
88 * overriding is not allowed (Status::OverridingNonVirtualError),
89 * otherwise it returns Status::OverridingNonVirtual
90 *
91 * - If no `override` or `final` keyword is specified for an existing virtual base,
92 * the override specifier is missing (Status::MissingOverrideOrFinalSpecifier).
93 *
94 * Returns Status::Valid if the combination is semantically correct.
95 */
96static inline Status checkFull(const QQmlPropertyData &overridingProperty,
97 const QQmlPropertyData *const existingProperty)
98{
99 const auto overrideKeyword = overridingProperty.doesOverride();
100 if (overrideKeyword && !existingProperty) {
101 return Status::MissingBase;
102 }
103
104 const auto minimalCheckRes = checkMinimal(existingProperty);
105 if (minimalCheckRes != Status::Valid) {
106 return minimalCheckRes;
107 }
108
109 // if the property doesn't exist we should have returned MissingBase or NoOverride already
110 Q_ASSERT(existingProperty);
111 if (overridingProperty.isFunction() != existingProperty->isFunction()) {
113 }
114
115 if (!existingProperty->isVirtual()) {
116 return overrideKeyword ? Status::OverridingNonVirtualError
118 }
119
120 const auto overrideOrFinal = overrideKeyword || overridingProperty.isFinal();
121 if (!overrideOrFinal) {
123 }
124
125 return Status::Valid;
126}
127
128static inline Status check(const QQmlPropertyData &overridingProperty,
129 const QQmlPropertyData *const existingProperty, CheckMode mode)
130{
131 Q_ASSERT(!hasInvalidModifierCombintation(overridingProperty));
132
133 switch (mode) {
135 return detail::checkMinimal(existingProperty);
136 case CheckMode::Full:
137 return detail::checkFull(overridingProperty, existingProperty);
138 default:
139 Q_UNREACHABLE_RETURN(Status::Unknown);
140 }
141}
142} // namespace detail
143
144Status handleOverride(QQmlPropertyData &overridingProperty, QQmlPropertyData *existingProperty,
145 CheckMode mode)
146{
147 const auto status = detail::check(overridingProperty, existingProperty, mode);
148
149 if (isValidOverride(status)) {
150 overridingProperty.markAsOverrideOf(existingProperty);
151 }
152 return status;
153}
154
155} // namespace OverrideSemantics
156
157static int metaObjectSignalCount(const QMetaObject *metaObject)
158{
159 int signalCount = 0;
160 for (const QMetaObject *obj = metaObject; obj; obj = obj->superClass())
161 signalCount += QMetaObjectPrivate::get(obj)->signalCount;
162 return signalCount;
163}
164
165QQmlPropertyData::Flags
166QQmlPropertyData::flagsForProperty(const QMetaProperty &p)
167{
168 QQmlPropertyData::Flags flags;
169
170 flags.setIsConstant(p.isConstant());
171 flags.setIsWritable(p.isWritable());
172 flags.setIsResettable(p.isResettable());
173 flags.setIsFinal(p.isFinal());
174 flags.setIsVirtual(p.isVirtual());
175 flags.setDoesOverride(p.isOverride());
176 flags.setIsRequired(p.isRequired());
177 flags.setIsBindable(p.isBindable());
178
179
180 const QMetaType metaType = p.metaType();
181 int propType = metaType.id();
182 if (p.isEnumType()) {
183 flags.setType(QQmlPropertyData::Flags::EnumType);
184 } else if (metaType.flags() & QMetaType::PointerToQObject) {
185 flags.setType(QQmlPropertyData::Flags::QObjectDerivedType);
186 } else if (propType == QMetaType::QVariant) {
187 flags.setType(QQmlPropertyData::Flags::QVariantType);
188 } else if (metaType.flags() & QMetaType::IsQmlList) {
189 flags.setType(QQmlPropertyData::Flags::QListType);
190 }
191
192 return flags;
193}
194
195void QQmlPropertyData::load(const QMetaProperty &p)
196{
197 Q_ASSERT(p.revision() <= std::numeric_limits<quint16>::max());
198 setCoreIndex(p.propertyIndex());
199 setNotifyIndex(QMetaObjectPrivate::signalIndex(p.notifySignal()));
200 setFlags(flagsForProperty(p));
201 setRevision(QTypeRevision::fromEncodedVersion(p.revision()));
202 QMetaType type = p.metaType();
203 setPropType(type);
204}
205
206void QQmlPropertyData::load(const QMetaMethod &m)
207{
208 setCoreIndex(m.methodIndex());
209 m_flags.setType(Flags::FunctionType);
210
211 // We need to set the constructor, signal, constant, arguments, V4Function, cloned flags.
212 // These are specific to methods and change with each method.
213 // The same QQmlPropertyData may be loaded with multiple methods in sequence.
214
215 switch (m.methodType()) {
216 case QMetaMethod::Signal:
217 m_flags.setIsSignal(true);
218 m_flags.setIsConstructor(false);
219 setPropType(m.returnMetaType());
220 break;
221 case QMetaMethod::Constructor:
222 m_flags.setIsSignal(false);
223 m_flags.setIsConstructor(true);
224 break;
225 default:
226 m_flags.setIsSignal(false);
227 m_flags.setIsConstructor(false);
228 setPropType(m.returnMetaType());
229 break;
230 }
231
232 m_flags.setIsConstant(m.isConst());
233
234 const int paramCount = m.parameterCount();
235 if (paramCount) {
236 m_flags.setHasArguments(true);
237 m_flags.setIsV4Function(
238 paramCount == 1 &&
239 m.parameterMetaType(0) == QMetaType::fromType<QQmlV4FunctionPtr>());
240 } else {
241 m_flags.setHasArguments(false);
242 m_flags.setIsV4Function(false);
243 }
244
245 m_flags.setIsCloned(m.attributes() & QMetaMethod::Cloned);
246
247 Q_ASSERT(m.revision() <= std::numeric_limits<quint16>::max());
248 setRevision(QTypeRevision::fromEncodedVersion(m.revision()));
249}
250
251Q_LOGGING_CATEGORY(qqmlPropertyCacheAppend, "qt.qml.propertyCache.append")
252
253/*!
254 \internal
255 Creates a standalone QQmlPropertyCache of \a metaObject. It is separate from the usual
256 QQmlPropertyCache hierarchy. It's parent is not equal to any other QQmlPropertyCache
257 created from QObject::staticMetaObject, for example.
258*/
259QQmlPropertyCache::Ptr QQmlPropertyCache::createStandalone(
260 const QMetaObject *metaObject, QTypeRevision metaObjectRevision)
261{
262 Q_ASSERT(metaObject);
263
264 Ptr result;
265 if (const QMetaObject *super = metaObject->superClass()) {
266 result = createStandalone(
267 super, metaObjectRevision)->copyAndAppend(metaObject, metaObjectRevision);
268 } else {
269 result.adopt(new QQmlPropertyCache(metaObject));
270 result->update(metaObject);
271 }
272
273 if (metaObjectRevision.isValid() && metaObjectRevision != QTypeRevision::zero()) {
274 // Set the revision of the meta object that this cache describes to be
275 // 'metaObjectRevision'. This is useful when constructing a property cache
276 // from a type that was created directly in C++, and not through QML. For such
277 // types, the revision for each recorded QMetaObject would normally be zero, which
278 // would exclude any revisioned properties.
279 for (int metaObjectOffset = 0; metaObjectOffset < result->allowedRevisionCache.size();
280 ++metaObjectOffset) {
281 result->allowedRevisionCache[metaObjectOffset] = metaObjectRevision;
282 }
283 }
284
285 return result;
286}
287
288QQmlPropertyCache::~QQmlPropertyCache()
289{
290 QQmlPropertyCacheMethodArguments *args = argumentsCache;
291 while (args) {
292 QQmlPropertyCacheMethodArguments *next = args->next;
293 delete args->names;
294 free(args);
295 args = next;
296 }
297
298 // We must clear this prior to releasing the parent incase it is a
299 // linked hash
300 stringCache.clear();
301}
302
303QQmlPropertyCache::Ptr QQmlPropertyCache::copy(const QQmlMetaObjectPointer &mo, int reserve) const
304{
305 QQmlPropertyCache::Ptr cache = QQmlPropertyCache::Ptr(
306 new QQmlPropertyCache(mo, _handleOverride), QQmlPropertyCache::Ptr::Adopt);
307 cache->_parent.reset(this);
308 cache->propertyIndexCacheStart = propertyIndexCache.size() + propertyIndexCacheStart;
309 cache->methodIndexCacheStart = methodIndexCache.size() + methodIndexCacheStart;
310 cache->signalHandlerIndexCacheStart = signalHandlerIndexCache.size() + signalHandlerIndexCacheStart;
311 cache->stringCache.linkAndReserve(stringCache, reserve);
312 cache->allowedRevisionCache = allowedRevisionCache;
313 cache->_defaultPropertyName = _defaultPropertyName;
314 cache->_listPropertyAssignBehavior = _listPropertyAssignBehavior;
315
316 return cache;
317}
318
319QQmlPropertyCache::Ptr QQmlPropertyCache::copy() const
320{
321 return copy(_metaObject, 0);
322}
323
324QQmlPropertyCache::Ptr QQmlPropertyCache::copyAndReserve(
325 int propertyCount, int methodCount, int signalCount, int enumCount) const
326{
327 QQmlPropertyCache::Ptr rv = copy(
328 QQmlMetaObjectPointer(), propertyCount + methodCount + signalCount);
329 rv->propertyIndexCache.reserve(propertyCount);
330 rv->methodIndexCache.reserve(methodCount);
331 rv->signalHandlerIndexCache.reserve(signalCount);
332 rv->enumCache.reserve(enumCount);
333 return rv;
334}
335
336QQmlPropertyCache::AppendResult
337QQmlPropertyCache::appendAlias(const QString &name, QQmlPropertyData::Flags flags, int coreIndex,
338 QMetaType propType, QTypeRevision version, int notifyIndex,
339 int encodedTargetIndex)
340{
341 QQmlPropertyData data;
342 data.setPropType(propType);
343 data.setCoreIndex(coreIndex);
344 data.setNotifyIndex(notifyIndex);
345 flags.setIsAlias(true);
346 data.setFlags(flags);
347 data.setAliasTarget(encodedTargetIndex);
348 data.setTypeVersion(version);
349
350 return appendPropertyAttr(name, std::move(data));
351}
352
353void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flags flags,
354 int coreIndex, const QMetaType *types,
355 const QList<QByteArray> &names)
356{
357 QQmlPropertyData data;
358 data.setPropType(QMetaType());
359 data.setCoreIndex(coreIndex);
360 data.setFlags(flags);
361 data.setArguments(nullptr);
362
363 QQmlPropertyData handler = data;
364 handler.m_flags.setIsSignalHandler(true);
365
366 if (types) {
367 const auto argumentCount = names.size();
368 QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
369 new (args->types) QMetaType; // Invalid return type
370 ::memcpy(args->types + 1, types, argumentCount * sizeof(QMetaType));
371 data.setArguments(args);
372 }
373
374 QQmlPropertyData *old = findNamedProperty(name);
375 const auto overrideStatus = _handleOverride(data, old, OverrideSemantics::CheckMode::Minimal);
376 maybeLog(overrideStatus, name);
377 // remove assert when checkMode is expanded and adjust handling correspondingly. For now it
378 // verifies that some code-path work in the same way as before introduction of virtual and
379 // override keywords
380 Q_ASSERT(overrideStatus == OverrideSemantics::Status::NoOverride
381 || overrideStatus == OverrideSemantics::Status::Valid
382 || overrideStatus == OverrideSemantics::Status::OverridingFinal);
383 if (overrideStatus == OverrideSemantics::Status::OverridingFinal) {
384 // TODO QTBUG-141728
385 // Insert the overridden member and its signal once more, to keep the counts in sync
386 methodIndexCache.append(*old);
387 handler = *old;
388 handler.m_flags.setIsSignalHandler(true);
389 signalHandlerIndexCache.append(handler);
390 return;
391 }
392
393 int methodIndex = methodIndexCache.size();
394 methodIndexCache.append(data);
395
396 int signalHandlerIndex = signalHandlerIndexCache.size();
397 signalHandlerIndexCache.append(handler);
398
399 const QString handlerName = QQmlSignalNames::signalNameToHandlerName(name);
400
401 setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex);
402 setNamedProperty(handlerName, signalHandlerIndex + signalOffset(),
403 signalHandlerIndexCache.data() + signalHandlerIndex);
404}
405
406void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flags flags,
407 int coreIndex, QMetaType returnType,
408 const QList<QByteArray> &names,
409 const QList<QMetaType> &parameterTypes)
410{
411 int argumentCount = names.size();
412
413 QQmlPropertyData data;
414 data.setPropType(returnType);
415 data.setCoreIndex(coreIndex);
416 data.setFlags(flags);
417 QQmlPropertyData *old = findNamedProperty(name);
418 const auto overrideStatus = _handleOverride(data, old, OverrideSemantics::CheckMode::Minimal);
419 maybeLog(overrideStatus, name);
420 // remove assert when checkMode is expanded and adjust handling correspondingly. For now it
421 // verifies that some code-path work in the same way as before introduction of virtual and
422 // override keywords
423 Q_ASSERT(overrideStatus == OverrideSemantics::Status::NoOverride
424 || overrideStatus == OverrideSemantics::Status::Valid
425 || overrideStatus == OverrideSemantics::Status::OverridingFinal);
426 if (overrideStatus == OverrideSemantics::Status::OverridingFinal) {
427 // TODO QTBUG-141728
428 // Insert the overridden member once more, to keep the counts in sync
429 methodIndexCache.append(*old);
430 return;
431 }
432
433 QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
434 new (args->types) QMetaType(returnType);
435 for (int ii = 0; ii < argumentCount; ++ii)
436 new (args->types + ii + 1) QMetaType(parameterTypes.at(ii));
437 data.setArguments(args);
438
439 int methodIndex = methodIndexCache.size();
440 methodIndexCache.append(data);
441
442 setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex);
443}
444
445void QQmlPropertyCache::appendEnum(const QString &name, const QList<QQmlEnumValue> &values)
446{
447 QQmlEnumData data;
448 data.name = name;
449 data.values = values;
450 enumCache.append(data);
451}
452
453// Returns this property cache's metaObject, creating it if necessary.
454const QMetaObject *QQmlPropertyCache::createMetaObject() const
455{
456 if (_metaObject.isNull()) {
457 QMetaObjectBuilder builder;
458 toMetaObjectBuilder(builder);
459 builder.setSuperClass(_parent->createMetaObject());
460 _metaObject.setSharedOnce(builder.toMetaObject());
461 }
462
463 return _metaObject.metaObject();
464}
465
466const QQmlPropertyData *QQmlPropertyCache::maybeUnresolvedProperty(int index) const
467{
468 if (index < 0 || index >= propertyCount())
469 return nullptr;
470
471 const QQmlPropertyData *rv = nullptr;
472 if (index < propertyIndexCacheStart)
473 return _parent->maybeUnresolvedProperty(index);
474 else
475 rv = const_cast<const QQmlPropertyData *>(&propertyIndexCache.at(index - propertyIndexCacheStart));
476 return rv;
477}
478
479const QQmlPropertyData *QQmlPropertyCache::defaultProperty() const
480{
481 return property(defaultPropertyName(), nullptr, nullptr);
482}
483
484void QQmlPropertyCache::setParent(QQmlPropertyCache::ConstPtr newParent)
485{
486 if (_parent != newParent)
487 _parent = std::move(newParent);
488}
489
490QQmlPropertyCache::Ptr
491QQmlPropertyCache::copyAndAppend(const QMetaObject *metaObject,
492 QTypeRevision typeVersion,
493 QQmlPropertyData::Flags propertyFlags,
494 QQmlPropertyData::Flags methodFlags,
495 QQmlPropertyData::Flags signalFlags) const
496{
497 Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
498
499 // Reserve enough space in the name hash for all the methods (including signals), all the
500 // signal handlers and all the properties. This assumes no name clashes, but this is the
501 // common case.
502 QQmlPropertyCache::Ptr rv = copy(
503 metaObject,
504 QMetaObjectPrivate::get(metaObject)->methodCount
505 + QMetaObjectPrivate::get(metaObject)->signalCount
506 + QMetaObjectPrivate::get(metaObject)->propertyCount);
507
508 rv->append(metaObject, typeVersion, propertyFlags, methodFlags, signalFlags);
509
510 return rv;
511}
512
513static QHashedString signalNameToHandlerName(const QHashedString &methodName)
514{
515 return QQmlSignalNames::signalNameToHandlerName(methodName);
516}
517
518static QHashedString signalNameToHandlerName(const QHashedCStringRef &methodName)
519{
520 return QQmlSignalNames::signalNameToHandlerName(
521 QLatin1StringView{ methodName.constData(), methodName.length() });
522}
523
524static inline std::pair<bool, int> deriveEncodingAndLength(const char *str)
525{
526 char utf8 = 0;
527 const char *cptr = str;
528 while (*cptr != 0) {
529 utf8 |= *cptr & 0x80;
530 ++cptr;
531 }
532 return std::make_pair(utf8, cptr - str);
533}
534
535void QQmlPropertyCache::append(const QMetaObject *metaObject,
536 QTypeRevision typeVersion,
537 QQmlPropertyData::Flags propertyFlags,
538 QQmlPropertyData::Flags methodFlags,
539 QQmlPropertyData::Flags signalFlags)
540{
541 allowedRevisionCache.append(QTypeRevision::zero());
542
543 int methodCount = metaObject->methodCount();
544 Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
545 int signalCount = metaObjectSignalCount(metaObject);
546 int classInfoCount = QMetaObjectPrivate::get(metaObject)->classInfoCount;
547
548 if (classInfoCount) {
549 int classInfoOffset = metaObject->classInfoOffset();
550 for (int ii = 0; ii < classInfoCount; ++ii) {
551 int idx = ii + classInfoOffset;
552 QMetaClassInfo mci = metaObject->classInfo(idx);
553 const char *name = mci.name();
554 if (0 == qstrcmp(name, "DefaultProperty")) {
555 _defaultPropertyName = QString::fromUtf8(mci.value());
556 } else if (0 == qstrcmp(name, "qt_QmlJSWrapperFactoryMethod")) {
557 const char * const factoryMethod = mci.value();
558 _jsFactoryMethodIndex = metaObject->indexOfSlot(factoryMethod);
559 if (_jsFactoryMethodIndex != -1)
560 _jsFactoryMethodIndex -= metaObject->methodOffset();
561 } else if (0 == qstrcmp(name, "QML.ListPropertyAssignBehavior")) {
562 _listPropertyAssignBehavior = mci.value();
563 }
564 }
565 }
566
567 //Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
568 static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
569 static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
570 static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
571 // These indices don't apply to gadgets, so don't block them.
572 // It is enough to check for QObject::staticMetaObject here because the loop below excludes
573 // methods of parent classes: It starts at metaObject->methodOffset()
574 const bool preventDestruction = (metaObject == &QObject::staticMetaObject);
575
576 int methodOffset = metaObject->methodOffset();
577 int signalOffset = signalCount - QMetaObjectPrivate::get(metaObject)->signalCount;
578
579 // update() should have reserved enough space in the vector that this doesn't cause a realloc
580 // and invalidate the stringCache.
581 methodIndexCache.resize(methodCount - methodIndexCacheStart);
582 signalHandlerIndexCache.resize(signalCount - signalHandlerIndexCacheStart);
583 int signalHandlerIndex = signalOffset;
584 for (int ii = methodOffset; ii < methodCount; ++ii) {
585 if (preventDestruction && (ii == destroyedIdx1 || ii == destroyedIdx2 || ii == deleteLaterIdx))
586 continue;
587 const QMetaMethod &m = metaObject->method(ii);
588 if (m.access() == QMetaMethod::Private)
589 continue;
590
591 // Extract method name
592 // It's safe to keep the raw name pointer
593 Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 7);
594
595 QQmlPropertyData *data = &methodIndexCache[ii - methodIndexCacheStart];
596 QQmlPropertyData *sigdata = nullptr;
597
598 if (m.methodType() == QMetaMethod::Signal)
599 data->setFlags(signalFlags);
600 else
601 data->setFlags(methodFlags);
602
603 data->load(m);
604
605 Q_ASSERT((allowedRevisionCache.size() - 1) < Q_INT16_MAX);
606 data->setMetaObjectOffset(allowedRevisionCache.size() - 1);
607
608 if (data->isSignal()) {
609 sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHandlerIndexCacheStart];
610 *sigdata = *data;
611 sigdata->m_flags.setIsSignalHandler(true);
612 }
613
614 const auto doSetNamedProperty = [&](const auto &methodName) {
615 QQmlPropertyData *old = nullptr;
616 if (StringCache::mapped_type *it = stringCache.value(methodName)) {
617 const auto overrideStatus = _handleOverride(*data, (old = it->second),
618 OverrideSemantics::CheckMode::Minimal);
619 maybeLog(overrideStatus, methodName);
620 // remove assert when checkMode is expanded and adjust handling correspondingly. For
621 // now it verifies that some code-path work in the same way as before introduction
622 // of virtual and override keywords
623 Q_ASSERT(overrideStatus == OverrideSemantics::Status::NoOverride
624 || overrideStatus == OverrideSemantics::Status::Valid
625 || overrideStatus == OverrideSemantics::Status::OverridingFinal);
626 if (overrideStatus == OverrideSemantics::Status::OverridingFinal) {
627 // TODO QTBUG-141728
628 *data = *old;
629 if (sigdata) {
630 // Keep the signal counts in sync,
631 // even if the "old" data has no real signal.
632 *sigdata = *old;
633 sigdata->m_flags.setIsSignalHandler(true);
634 ++signalHandlerIndex;
635 }
636 return;
637 }
638 }
639
640 setNamedProperty(methodName, ii, data);
641
642 if (data->isSignal()) {
643
644 // TODO: Remove this once we can. Signals should not be overridable.
645 if constexpr (std::is_same_v<std::decay_t<decltype(methodName)>, QHashedCStringRef>)
646 data->m_flags.setIsOverridableSignal(true);
647
648 setNamedProperty(signalNameToHandlerName(methodName), ii, sigdata);
649 ++signalHandlerIndex;
650 }
651 };
652
653 const char *str = m.nameView().constData();
654 const auto [isUtf8, len] = deriveEncodingAndLength(str);
655 if (isUtf8)
656 doSetNamedProperty(QHashedString(QString::fromUtf8(str, len)));
657 else
658 doSetNamedProperty(QHashedCStringRef(str, len));
659 }
660
661 int propCount = metaObject->propertyCount();
662 int propOffset = metaObject->propertyOffset();
663
664 // update() should have reserved enough space in the vector that this doesn't cause a realloc
665 // and invalidate the stringCache.
666 propertyIndexCache.resize(propCount - propertyIndexCacheStart);
667 for (int ii = propOffset; ii < propCount; ++ii) {
668 QMetaProperty p = metaObject->property(ii);
669 if (!p.isScriptable())
670 continue;
671
672 // TODO QTBUG-141728
673 QQmlPropertyData *data = &propertyIndexCache[ii - propertyIndexCacheStart];
674
675 data->setFlags(propertyFlags);
676 data->load(p);
677 data->setTypeVersion(typeVersion);
678
679 Q_ASSERT((allowedRevisionCache.size() - 1) < Q_INT16_MAX);
680 data->setMetaObjectOffset(allowedRevisionCache.size() - 1);
681
682 const auto doSetNamedProperty = [this](const auto &propName, int index, auto *propData) {
683 QQmlPropertyData *existingPropData = findNamedProperty(propName);
684 const auto overrideStatus = _handleOverride(*propData, existingPropData,
685 OverrideSemantics::CheckMode::Full);
686 maybeLog(overrideStatus, propName);
687 if (!OverrideSemantics::isValidOverride(overrideStatus)) {
688 if (existingPropData) {
689 // TODO QTBUG-141728
690 *propData = *existingPropData;
691 }
692 return;
693 }
694
695 setNamedProperty(propName, index, propData);
696 };
697
698 const char *str = p.name();
699 const auto [isUtf8, len] = deriveEncodingAndLength(str);
700 if (isUtf8)
701 doSetNamedProperty(QHashedString(QString::fromUtf8(str, len)), ii, data);
702 else
703 doSetNamedProperty(QHashedCStringRef(str, len), ii, data);
704
705 bool isGadget = true;
706 for (const QMetaObject *it = metaObject; it != nullptr; it = it->superClass()) {
707 if (it == &QObject::staticMetaObject)
708 isGadget = false;
709 }
710
711 // otherwise always dispatch over a 'normal' meta-call so the QQmlValueType can intercept
712 if (!isGadget && !data->isAlias())
713 data->trySetStaticMetaCallFunction(metaObject->d.static_metacall, ii - propOffset);
714 }
715}
716
717void QQmlPropertyCache::update(const QMetaObject *metaObject)
718{
719 Q_ASSERT(metaObject);
720 stringCache.clear();
721
722 // Preallocate enough space in the index caches for all the properties/methods/signals that
723 // are not cached in a parent cache so that the caches never need to be reallocated as this
724 // would invalidate pointers stored in the stringCache.
725 int pc = metaObject->propertyCount();
726 int mc = metaObject->methodCount();
727 int sc = metaObjectSignalCount(metaObject);
728 propertyIndexCache.reserve(pc - propertyIndexCacheStart);
729 methodIndexCache.reserve(mc - methodIndexCacheStart);
730 signalHandlerIndexCache.reserve(sc - signalHandlerIndexCacheStart);
731
732 // Reserve enough space in the stringCache for all properties/methods/signals including those
733 // cached in a parent cache.
734 stringCache.reserve(pc + mc + sc);
735
736 if (metaObject)
737 append(metaObject, QTypeRevision());
738}
739
740/*! \internal
741 invalidates and updates the PropertyCache if the QMetaObject has changed.
742 This function is used in the tooling to update dynamic properties.
743*/
744void QQmlPropertyCache::invalidate(const QMetaObject *metaObject)
745{
746 propertyIndexCache.clear();
747 methodIndexCache.clear();
748 signalHandlerIndexCache.clear();
749
750 argumentsCache = nullptr;
751
752 int pc = metaObject->propertyCount();
753 int mc = metaObject->methodCount();
754 int sc = metaObjectSignalCount(metaObject);
755 int reserve = pc + mc + sc;
756
757 if (parent()) {
758 propertyIndexCacheStart = parent()->propertyIndexCache.size() + parent()->propertyIndexCacheStart;
759 methodIndexCacheStart = parent()->methodIndexCache.size() + parent()->methodIndexCacheStart;
760 signalHandlerIndexCacheStart = parent()->signalHandlerIndexCache.size() + parent()->signalHandlerIndexCacheStart;
761 stringCache.linkAndReserve(parent()->stringCache, reserve);
762 append(metaObject, QTypeRevision());
763 } else {
764 propertyIndexCacheStart = 0;
765 methodIndexCacheStart = 0;
766 signalHandlerIndexCacheStart = 0;
767 update(metaObject);
768 }
769}
770
771const QQmlPropertyData *QQmlPropertyCache::findProperty(
772 StringCache::ConstIterator it, QObject *object,
773 const QQmlRefPointer<QQmlContextData> &context) const
774{
775 QQmlData *data = (object ? QQmlData::get(object) : nullptr);
776 const QQmlVMEMetaObject *vmemo = nullptr;
777 if (data && data->hasVMEMetaObject) {
778 QObjectPrivate *op = QObjectPrivate::get(object);
779 vmemo = static_cast<const QQmlVMEMetaObject *>(op->metaObject);
780 }
781 return findProperty(it, vmemo, context);
782}
783
784namespace {
785
786inline bool contextHasNoExtensions(const QQmlRefPointer<QQmlContextData> &context)
787{
788 // This context has no extension if its parent is the engine's rootContext,
789 // which has children but no imports
790 const QQmlRefPointer<QQmlContextData> parent = context->parent();
791 return (!parent || !parent->imports());
792}
793
794inline int maximumIndexForProperty(const QQmlPropertyData *prop, const int methodCount, const int signalCount, const int propertyCount)
795{
796 return prop->isFunction() ? methodCount
797 : prop->isSignalHandler() ? signalCount
798 : propertyCount;
799}
800
801}
802
803const QQmlPropertyData *QQmlPropertyCache::findProperty(
804 StringCache::ConstIterator it, const QQmlVMEMetaObject *vmemo,
805 const QQmlRefPointer<QQmlContextData> &context) const
806{
807 StringCache::ConstIterator end = stringCache.end();
808
809 if (it != end) {
810 const QQmlPropertyData *result = it.value().second;
811
812 // If there exists a typed property (not a function or signal handler), of the
813 // right name available to the specified context, we need to return that
814 // property rather than any subsequent override
815
816 if (vmemo && context && !contextHasNoExtensions(context)) {
817 // Find the meta-object that corresponds to the supplied context
818 do {
819 if (vmemo->contextData() == context)
820 break;
821
822 vmemo = vmemo->parentVMEMetaObject();
823 } while (vmemo);
824 }
825
826 if (vmemo) {
827 const int methodCount = vmemo->cache->methodCount();
828 const int signalCount = vmemo->cache->signalCount();
829 const int propertyCount = vmemo->cache->propertyCount();
830
831 // Ensure that the property we resolve to is accessible from this meta-object
832 do {
833 const StringCache::mapped_type &property(it.value());
834
835 if (property.first < maximumIndexForProperty(property.second, methodCount, signalCount, propertyCount)) {
836 // This property is available in the specified context
837 if (property.second->isFunction() || property.second->isSignalHandler()) {
838 // Prefer the earlier resolution
839 } else {
840 // Prefer the typed property to any previous property found
841 result = property.second;
842 }
843 break;
844 }
845
846 // See if there is a better candidate
847 it = stringCache.findNext(it);
848 } while (it != end);
849 }
850
851 return result;
852 }
853
854 return nullptr;
855}
856
857// Note, this function is called when adding aliases, hence data.isEnum() can possibly be true
858QQmlPropertyCache::AppendResult QQmlPropertyCache::appendPropertyAttr(const QString &name,
859 QQmlPropertyData &&data)
860{
861 QQmlPropertyData *old = findNamedProperty(name);
862 const auto overrideStatus = _handleOverride(data, old, OverrideSemantics::CheckMode::Full);
863 maybeLog(overrideStatus, name);
864 if (!OverrideSemantics::isValidOverride(overrideStatus)) {
865 // TODO QTBUG-141728
866 // Insert the overridden member once more, to keep the counts in sync
867 propertyIndexCache.append(old ? *old : data);
868 return q23::make_unexpected(overrideStatus);
869 }
870
871 const int index = propertyIndexCache.size();
872 propertyIndexCache.append(std::move(data));
873
874 setNamedProperty(name, index + propertyOffset(), propertyIndexCache.data() + index);
875 return {};
876}
877
878void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor)
879{
880 Q_ASSERT(predecessor != this);
881
882 if (!predecessor) {
883 return;
884 }
885
886 setOverrideIndexIsProperty(!predecessor->isFunction());
887 setOverrideIndex(predecessor->coreIndex());
888 // propagate "virtuality"
889 m_flags.setIsVirtual(predecessor->isVirtual());
890 predecessor->m_flags.setIsOverridden(true);
891 Q_ASSERT(predecessor->isOverridden());
892 return;
893}
894
895QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(
896 int argc, const QList<QByteArray> &names)
897{
898 typedef QQmlPropertyCacheMethodArguments A;
899 A *args = static_cast<A *>(malloc(sizeof(A) + argc * sizeof(QMetaType)));
900 args->names = argc ? new QList<QByteArray>(names) : nullptr;
901 args->next = argumentsCache;
902 argumentsCache = args;
903 return args;
904}
905
906QString QQmlPropertyCache::signalParameterStringForJS(
907 const QList<QByteArray> &parameterNameList, QString *errorString)
908{
909 bool unnamedParameter = false;
910 QString parameters;
911
912 const qsizetype count = parameterNameList.size();
913 if (count > std::numeric_limits<quint16>::max())
914 *errorString = QCoreApplication::translate("QQmlRewrite", "Signal has an excessive number of parameters: %1").arg(count);
915
916 for (qsizetype i = 0; i < count; ++i) {
917 if (i > 0)
918 parameters += QLatin1Char(',');
919 const QByteArray &param = parameterNameList.at(i);
920 if (param.isEmpty()) {
921 unnamedParameter = true;
922 } else if (unnamedParameter) {
923 if (errorString)
924 *errorString = QCoreApplication::translate("QQmlRewrite", "Signal uses unnamed parameter followed by named parameter.");
925 return QString();
926 } else if (QV4::Compiler::Codegen::isNameGlobal(param)) {
927 if (errorString)
928 *errorString = QCoreApplication::translate("QQmlRewrite", "Signal parameter \"%1\" hides global variable.").arg(QString::fromUtf8(param));
929 return QString();
930 }
931 parameters += QString::fromUtf8(param);
932 }
933
934 return parameters;
935}
936
937int QQmlPropertyCache::originalClone(int index) const
938{
939 while (signal(index)->isCloned())
940 --index;
941 return index;
942}
943
944int QQmlPropertyCache::originalClone(const QObject *object, int index)
945{
946 QQmlData *data = QQmlData::get(object);
947 if (data && data->propertyCache) {
948 const QQmlPropertyCache *cache = data->propertyCache.data();
949 const QQmlPropertyData *sig = cache->signal(index);
950 while (sig && sig->isCloned()) {
951 --index;
952 sig = cache->signal(index);
953 }
954 } else {
955 while (QMetaObjectPrivate::signal(object->metaObject(), index).attributes() & QMetaMethod::Cloned)
956 --index;
957 }
958 return index;
959}
960
961template<typename T>
962static QQmlPropertyData qQmlPropertyCacheCreate(const QMetaObject *metaObject, const T& propertyName)
963{
964 Q_ASSERT(metaObject);
965
966 QQmlPropertyData rv;
967
968 /* It's important to check the method list before checking for properties;
969 * otherwise, if the meta object is dynamic, a property will be created even
970 * if not found and it might obscure a method having the same name. */
971
972 //Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
973 static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
974 static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
975 static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
976 // These indices don't apply to gadgets, so don't block them.
977 const bool preventDestruction = metaObject->superClass() || metaObject == &QObject::staticMetaObject;
978
979 int methodCount = metaObject->methodCount();
980 for (int ii = methodCount - 1; ii >= 0; --ii) {
981 if (preventDestruction && (ii == destroyedIdx1 || ii == destroyedIdx2 || ii == deleteLaterIdx))
982 continue;
983 QMetaMethod m = metaObject->method(ii);
984 if (m.access() == QMetaMethod::Private)
985 continue;
986
987 if (m.name() == propertyName) {
988 rv.load(m);
989 return rv;
990 }
991 }
992
993 {
994 const QMetaObject *cmo = metaObject;
995 while (cmo) {
996 int idx = cmo->indexOfProperty(propertyName);
997 if (idx != -1) {
998 QMetaProperty p = cmo->property(idx);
999 if (p.isScriptable()) {
1000 rv.load(p);
1001 return rv;
1002 } else {
1003 bool changed = false;
1004 while (cmo && cmo->propertyOffset() >= idx) {
1005 cmo = cmo->superClass();
1006 changed = true;
1007 }
1008 /* If the "cmo" variable didn't change, set it to 0 to
1009 * avoid running into an infinite loop */
1010 if (!changed) cmo = nullptr;
1011 }
1012 } else {
1013 cmo = nullptr;
1014 }
1015 }
1016 }
1017 return rv;
1018}
1019
1020static inline const char *qQmlPropertyCacheToString(QLatin1String string)
1021{
1022 return string.data();
1023}
1024
1025static inline QByteArray qQmlPropertyCacheToString(QStringView string)
1026{
1027 return string.toUtf8();
1028}
1029
1030static inline QByteArray qQmlPropertyCacheToString(const QV4::String *string)
1031{
1032 return string->toQString().toUtf8();
1033}
1034
1035template<typename T>
1036const QQmlPropertyData *
1037qQmlPropertyCacheProperty(QObject *obj, T name, const QQmlRefPointer<QQmlContextData> &context,
1038 QQmlPropertyData *local)
1039{
1040 const QQmlPropertyCache *cache = nullptr;
1041
1042 QQmlData *ddata = QQmlData::get(obj, false);
1043
1044 if (ddata && ddata->propertyCache) {
1045 cache = ddata->propertyCache.data();
1046 } else if (auto newCache = QQmlMetaType::propertyCache(obj)) {
1047 cache = newCache.data();
1048 ddata = QQmlData::get(obj, true);
1049 ddata->propertyCache = std::move(newCache);
1050 }
1051
1052 const QQmlPropertyData *rv = nullptr;
1053
1054 if (cache) {
1055 rv = cache->property(name, obj, context);
1056 } else if (local) {
1057 *local = qQmlPropertyCacheCreate(obj->metaObject(), qQmlPropertyCacheToString(name));
1058 if (local->isValid())
1059 rv = local;
1060 }
1061
1062 return rv;
1063}
1064
1065const QQmlPropertyData *QQmlPropertyCache::property(
1066 QObject *obj, const QV4::String *name, const QQmlRefPointer<QQmlContextData> &context,
1067 QQmlPropertyData *local)
1068{
1069 return qQmlPropertyCacheProperty<const QV4::String *>(obj, name, context, local);
1070}
1071
1072const QQmlPropertyData *QQmlPropertyCache::property(
1073 QObject *obj, QStringView name, const QQmlRefPointer<QQmlContextData> &context,
1074 QQmlPropertyData *local)
1075{
1076 return qQmlPropertyCacheProperty<const QStringView &>(obj, name, context, local);
1077}
1078
1079const QQmlPropertyData *QQmlPropertyCache::property(
1080 QObject *obj, const QLatin1String &name, const QQmlRefPointer<QQmlContextData> &context,
1081 QQmlPropertyData *local)
1082{
1083 return qQmlPropertyCacheProperty<const QLatin1String &>(obj, name, context, local);
1084}
1085
1086// this function is copied from qmetaobject.cpp
1087static inline const QByteArray stringData(const QMetaObject *mo, int index)
1088{
1089 uint offset = mo->d.stringdata[2*index];
1090 uint length = mo->d.stringdata[2*index + 1];
1091 const char *string = reinterpret_cast<const char *>(mo->d.stringdata) + offset;
1092 return QByteArray::fromRawData(string, length);
1093}
1094
1095const char *QQmlPropertyCache::className() const
1096{
1097 if (const QMetaObject *mo = _metaObject.metaObject())
1098 return mo->className();
1099 else
1100 return _dynamicClassName.constData();
1101}
1102
1103void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) const
1104{
1105 struct Sort { static bool lt(const std::pair<QString, const QQmlPropertyData *> &lhs,
1106 const std::pair<QString, const QQmlPropertyData *> &rhs) {
1107 return lhs.second->coreIndex() < rhs.second->coreIndex();
1108 } };
1109
1110 struct Insert { static void in(const QQmlPropertyCache *This,
1111 QList<std::pair<QString, const QQmlPropertyData *> > &properties,
1112 QList<std::pair<QString, const QQmlPropertyData *> > &methods,
1113 StringCache::ConstIterator iter, const QQmlPropertyData *data) {
1114 if (data->isSignalHandler())
1115 return;
1116
1117 if (data->isFunction()) {
1118 if (data->coreIndex() < This->methodIndexCacheStart)
1119 return;
1120
1121 std::pair<QString, const QQmlPropertyData *> entry = std::make_pair((QString)iter.key(), data);
1122 // Overrides can cause the entry to already exist
1123 if (!methods.contains(entry)) methods.append(entry);
1124
1125 data = This->overrideData(data);
1126 if (data && !data->isFunction()) Insert::in(This, properties, methods, iter, data);
1127 } else {
1128 if (data->coreIndex() < This->propertyIndexCacheStart)
1129 return;
1130
1131 std::pair<QString, const QQmlPropertyData *> entry = std::make_pair((QString)iter.key(), data);
1132 // Overrides can cause the entry to already exist
1133 if (!properties.contains(entry)) properties.append(entry);
1134
1135 data = This->overrideData(data);
1136 if (data) Insert::in(This, properties, methods, iter, data);
1137 }
1138
1139 } };
1140
1141 builder.setClassName(_dynamicClassName);
1142
1143 QList<std::pair<QString, const QQmlPropertyData *> > properties;
1144 QList<std::pair<QString, const QQmlPropertyData *> > methods;
1145
1146 for (StringCache::ConstIterator iter = stringCache.begin(), cend = stringCache.end(); iter != cend; ++iter)
1147 Insert::in(this, properties, methods, iter, iter.value().second);
1148
1149 // Any invalid overrides are not linked by name into the properties and methods hashes.
1150 // Therefore there can be more properties and methods than present in the hashes.
1151 Q_ASSERT(properties.size() <= propertyIndexCache.size());
1152 Q_ASSERT(methods.size() <= methodIndexCache.size());
1153
1154 std::sort(properties.begin(), properties.end(), Sort::lt);
1155 std::sort(methods.begin(), methods.end(), Sort::lt);
1156
1157 for (int ii = 0; ii < properties.size(); ++ii) {
1158 const QQmlPropertyData *data = properties.at(ii).second;
1159
1160 int notifierId = -1;
1161 if (data->notifyIndex() != -1)
1162 notifierId = data->notifyIndex() - signalHandlerIndexCacheStart;
1163
1164 QMetaPropertyBuilder property = builder.addProperty(properties.at(ii).first.toUtf8(),
1165 data->propType().name(),
1166 data->propType(),
1167 notifierId);
1168
1169 property.setReadable(true);
1170 property.setWritable(data->isWritable());
1171 property.setResettable(data->isResettable());
1172 property.setBindable(data->notifiesViaBindable());
1173 property.setAlias(data->isAlias());
1174 }
1175
1176 for (int ii = 0; ii < methods.size(); ++ii) {
1177 const QQmlPropertyData *data = methods.at(ii).second;
1178
1179 QByteArray returnType;
1180 if (data->propType().isValid())
1181 returnType = data->propType().name();
1182
1183 QByteArray signature;
1184 // '+=' reserves extra capacity. Follow-up appending will be probably free.
1185 signature += methods.at(ii).first.toUtf8() + '(';
1186
1187 QQmlPropertyCacheMethodArguments *arguments = nullptr;
1188 if (data->hasArguments()) {
1189 arguments = data->arguments();
1190 for (int ii = 0, end = arguments->names ? arguments->names->size() : 0;
1191 ii < end; ++ii) {
1192 if (ii != 0)
1193 signature.append(',');
1194 signature.append(arguments->types[1 + ii].name());
1195 }
1196 }
1197
1198 signature.append(')');
1199
1200 QMetaMethodBuilder method;
1201 if (data->isSignal()) {
1202 method = builder.addSignal(signature);
1203 } else {
1204 method = builder.addSlot(signature);
1205 }
1206 method.setAccess(QMetaMethod::Public);
1207
1208 if (arguments && arguments->names)
1209 method.setParameterNames(*arguments->names);
1210
1211 if (!returnType.isEmpty())
1212 method.setReturnType(returnType);
1213 }
1214
1215 for (int ii = 0; ii < enumCache.size(); ++ii) {
1216 const QQmlEnumData &enumData = enumCache.at(ii);
1217 QMetaEnumBuilder enumeration = builder.addEnumerator(enumData.name.toUtf8());
1218 enumeration.setIsScoped(true);
1219 for (int jj = 0; jj < enumData.values.size(); ++jj) {
1220 const QQmlEnumValue &value = enumData.values.at(jj);
1221 enumeration.addKey(value.namedValue.toUtf8(), value.value);
1222 }
1223 }
1224
1225 if (!_defaultPropertyName.isEmpty()) {
1226 const QQmlPropertyData *dp = property(_defaultPropertyName, nullptr, nullptr);
1227 if (dp && dp->coreIndex() >= propertyIndexCacheStart) {
1228 Q_ASSERT(!dp->isFunction());
1229 builder.addClassInfo("DefaultProperty", _defaultPropertyName.toUtf8());
1230 }
1231 }
1232
1233 if (!_listPropertyAssignBehavior.isEmpty())
1234 builder.addClassInfo("QML.ListPropertyAssignBehavior", _listPropertyAssignBehavior);
1235}
1236
1237namespace {
1238template <typename StringVisitor, typename TypeInfoVisitor>
1239int visitMethods(const QMetaObject &mo, int methodOffset, int methodCount,
1240 StringVisitor visitString, TypeInfoVisitor visitTypeInfo)
1241{
1242 int fieldsForParameterData = 0;
1243
1244 bool hasOldStyleRevisionedMethods = false;
1245
1246 for (int i = 0; i < methodCount; ++i) {
1247 const int handle = methodOffset + i * QMetaObjectPrivate::IntsPerMethod;
1248
1249 const uint flags = mo.d.data[handle + 4];
1250 if (flags & MethodRevisioned) {
1251 if (mo.d.data[0] < 13)
1252 hasOldStyleRevisionedMethods = true;
1253 else
1254 fieldsForParameterData += 1; // revision
1255 }
1256
1257 visitString(mo.d.data[handle + 0]); // name
1258 visitString(mo.d.data[handle + 3]); // tag
1259
1260 const int argc = mo.d.data[handle + 1];
1261 const int paramIndex = mo.d.data[handle + 2];
1262
1263 fieldsForParameterData += argc * 2; // type and name
1264 fieldsForParameterData += 1; // + return type
1265
1266 // return type + args
1267 for (int i = 0; i < 1 + argc; ++i) {
1268 // type name (maybe)
1269 visitTypeInfo(mo.d.data[paramIndex + i]);
1270
1271 // parameter name
1272 if (i > 0)
1273 visitString(mo.d.data[paramIndex + argc + i]);
1274 }
1275 }
1276
1277 int fieldsForRevisions = 0;
1278 if (hasOldStyleRevisionedMethods)
1279 fieldsForRevisions = methodCount;
1280
1281 return methodCount * QMetaObjectPrivate::IntsPerMethod
1282 + fieldsForRevisions + fieldsForParameterData;
1283}
1284
1285template <typename StringVisitor, typename TypeInfoVisitor>
1286int visitProperties(const QMetaObject &mo, StringVisitor visitString, TypeInfoVisitor visitTypeInfo)
1287{
1288 const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
1289
1290 for (int i = 0; i < priv->propertyCount; ++i) {
1291 const int handle = priv->propertyData + i * QMetaObjectPrivate::IntsPerProperty;
1292
1293 visitString(mo.d.data[handle]); // name
1294 visitTypeInfo(mo.d.data[handle + 1]);
1295 }
1296
1297 return priv->propertyCount * QMetaObjectPrivate::IntsPerProperty;
1298}
1299
1300template <typename StringVisitor>
1301int visitClassInfo(const QMetaObject &mo, StringVisitor visitString)
1302{
1303 const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
1304 const int intsPerClassInfo = 2;
1305
1306 for (int i = 0; i < priv->classInfoCount; ++i) {
1307 const int handle = priv->classInfoData + i * intsPerClassInfo;
1308
1309 visitString(mo.d.data[handle]); // key
1310 visitString(mo.d.data[handle + 1]); // value
1311 }
1312
1313 return priv->classInfoCount * intsPerClassInfo;
1314}
1315
1316template <typename StringVisitor>
1317int visitEnumerations(const QMetaObject &mo, StringVisitor visitString)
1318{
1319 const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
1320
1321 int fieldCount = priv->enumeratorCount * QMetaObjectPrivate::IntsPerEnum;
1322
1323 for (int i = 0; i < priv->enumeratorCount; ++i) {
1324 const uint *enumeratorData = mo.d.data + priv->enumeratorData + i * QMetaObjectPrivate::IntsPerEnum;
1325
1326 const uint keyCount = enumeratorData[3];
1327 fieldCount += keyCount * 2;
1328
1329 visitString(enumeratorData[0]); // name
1330 visitString(enumeratorData[1]); // enum name
1331
1332 const uint keyOffset = enumeratorData[4];
1333
1334 for (uint j = 0; j < keyCount; ++j) {
1335 visitString(mo.d.data[keyOffset + 2 * j]);
1336 }
1337 }
1338
1339 return fieldCount;
1340}
1341
1342template <typename StringVisitor>
1343int countMetaObjectFields(const QMetaObject &mo, StringVisitor stringVisitor)
1344{
1345 const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
1346
1347 const auto typeInfoVisitor = [&stringVisitor](uint typeInfo) {
1348 if (typeInfo & IsUnresolvedType)
1349 stringVisitor(typeInfo & TypeNameIndexMask);
1350 };
1351
1352 int fieldCount = MetaObjectPrivateFieldCount;
1353
1354 fieldCount += visitMethods(mo, priv->methodData, priv->methodCount, stringVisitor,
1355 typeInfoVisitor);
1356 fieldCount += visitMethods(mo, priv->constructorData, priv->constructorCount, stringVisitor,
1357 typeInfoVisitor);
1358
1359 fieldCount += visitProperties(mo, stringVisitor, typeInfoVisitor);
1360 fieldCount += visitClassInfo(mo, stringVisitor);
1361 fieldCount += visitEnumerations(mo, stringVisitor);
1362
1363 return fieldCount;
1364}
1365
1366} // anonymous namespace
1367
1368// Temporary leniency to allow for smooth transition of moc change - QTBUG-142186
1369static_assert(QMetaObjectPrivate::OutputRevision == 13 || QMetaObjectPrivate::OutputRevision == 14,
1370 "Check and adjust determineMetaObjectSizes");
1371
1372bool QQmlPropertyCache::determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount,
1373 int *stringCount)
1374{
1375 const QMetaObjectPrivate *priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
1376 if (priv->revision != QMetaObjectPrivate::OutputRevision)
1377 return false;
1378
1379 uint highestStringIndex = 0;
1380 const auto stringIndexVisitor = [&highestStringIndex](uint index) {
1381 highestStringIndex = qMax(highestStringIndex, index);
1382 };
1383
1384 *fieldCount = countMetaObjectFields(mo, stringIndexVisitor);
1385 *stringCount = highestStringIndex + 1;
1386
1387 return true;
1388}
1389
1390bool QQmlPropertyCache::addToHash(QCryptographicHash &hash, const QMetaObject &mo)
1391{
1392 int fieldCount = 0;
1393 int stringCount = 0;
1394 if (!determineMetaObjectSizes(mo, &fieldCount, &stringCount)) {
1395 return false;
1396 }
1397
1398 hash.addData({reinterpret_cast<const char *>(mo.d.data), qsizetype(fieldCount * sizeof(uint))});
1399 for (int i = 0; i < stringCount; ++i) {
1400 hash.addData(stringData(&mo, i));
1401 }
1402
1403 return true;
1404}
1405
1406QByteArray QQmlPropertyCache::checksum(QHash<quintptr, QByteArray> *checksums, bool *ok) const
1407{
1408 auto it = checksums->constFind(quintptr(this));
1409 if (it != checksums->constEnd()) {
1410 *ok = true;
1411 return *it;
1412 }
1413
1414 // Generate a checksum on the meta-object data only on C++ types.
1415 if (_metaObject.isShared()) {
1416 *ok = false;
1417 return QByteArray();
1418 }
1419
1420 QCryptographicHash hash(QCryptographicHash::Md5);
1421
1422 if (_parent) {
1423 hash.addData(_parent->checksum(checksums, ok));
1424 if (!*ok)
1425 return QByteArray();
1426 }
1427
1428 if (!addToHash(hash, *_metaObject.metaObject())) {
1429 *ok = false;
1430 return QByteArray();
1431 }
1432
1433 const QByteArray result = hash.result();
1434 if (result.isEmpty()) {
1435 *ok = false;
1436 } else {
1437 *ok = true;
1438 checksums->insert(quintptr(this), result);
1439 }
1440 return result;
1441}
1442
1443/*! \internal
1444 \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
1445 This is different from QMetaMethod::methodIndex().
1446*/
1447QList<QByteArray> QQmlPropertyCache::signalParameterNames(int index) const
1448{
1449 const QQmlPropertyData *signalData = signal(index);
1450 if (signalData && signalData->hasArguments()) {
1451 QQmlPropertyCacheMethodArguments *args = (QQmlPropertyCacheMethodArguments *)signalData->arguments();
1452 if (args && args->names)
1453 return *args->names;
1454 const QMetaMethod &method = QMetaObjectPrivate::signal(firstCppMetaObject(), index);
1455 return method.parameterNames();
1456 }
1457 return QList<QByteArray>();
1458}
1459
1460QT_END_NAMESPACE
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)
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)