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