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::rebased(const ConstPtr &parent) const
326{
327 // Leave the meta-object empty so createMetaObject() rebuilds it lazily from our own members on
328 // top of the new parent's meta-object. Seeding it with the parent's meta-object would drop our
329 // own members from the derived meta-object.
330 QQmlPropertyCache::Ptr cache = QQmlPropertyCache::Ptr(
331 new QQmlPropertyCache(QQmlMetaObjectPointer(), _handleOverride),
332 QQmlPropertyCache::Ptr::Adopt);
333
334 cache->_parent = parent;
335
336 const int oldPropertyStart = propertyIndexCacheStart;
337 const int oldMethodStart = methodIndexCacheStart;
338 const int oldSignalStart = signalHandlerIndexCacheStart;
339
340 const int deltaProperty = parent->propertyCount() - oldPropertyStart;
341 const int deltaMethod = parent->methodCount() - oldMethodStart;
342 const int deltaSignal = parent->signalCount() - oldSignalStart;
343
344 cache->propertyIndexCacheStart = parent->propertyCount();
345 cache->propertyIndexCache = propertyIndexCache;
346
347 cache->methodIndexCacheStart = parent->methodCount();
348 cache->methodIndexCache = methodIndexCache;
349
350 cache->signalHandlerIndexCacheStart = parent->signalCount();
351 cache->signalHandlerIndexCache = signalHandlerIndexCache;
352
353 cache->enumCache = enumCache;
354
355 const auto shifted = [](int value, int threshold, int delta) {
356 return value >= threshold ? value + delta : value;
357 };
358
359 const auto shiftData = [&](QQmlPropertyData &data, int coreDelta, int coreThreshold) {
360 if (data.coreIndex() >= 0)
361 data.setCoreIndex(shifted(data.coreIndex(), coreThreshold, coreDelta));
362 if (data.notifyIndex() >= 0)
363 data.setNotifyIndex(shifted(data.notifyIndex(), oldSignalStart, deltaSignal));
364 };
365
366 for (QQmlPropertyData &data : cache->propertyIndexCache)
367 shiftData(data, deltaProperty, oldPropertyStart);
368 for (QQmlPropertyData &data : cache->methodIndexCache)
369 shiftData(data, deltaMethod, oldMethodStart);
370 for (QQmlPropertyData &data : cache->signalHandlerIndexCache)
371 shiftData(data, deltaMethod, oldMethodStart);
372
373 cache->stringCache.linkAndReserve(
374 parent->stringCache, ownPropertyCount() + ownMethodCount() + ownSignalCount());
375
376 for (auto it = stringCache.begin(), end = stringCache.end(); it != end; ++it) {
377 const QQmlPropertyData *myData = it.value().second;
378 int index = it.value().first;
379
380 QQmlPropertyData *copyData = nullptr;
381 if (myData->isSignalHandler()) {
382 index = shifted(index, oldSignalStart, deltaSignal);
383 copyData = cache->signalHandlerIndexCache.data() + (index - cache->signalOffset());
384 } else if (myData->isFunction()) {
385 index = shifted(index, oldMethodStart, deltaMethod);
386 copyData = cache->methodIndexCache.data() + (index - cache->methodOffset());
387 } else {
388 index = shifted(index, oldPropertyStart, deltaProperty);
389 copyData = cache->propertyIndexCache.data() + (index - cache->propertyOffset());
390 }
391
392 if (const QQmlPropertyData *overridden = parent->findNamedProperty(it.key()))
393 copyData->setOverrideIndex(overridden->coreIndex());
394 else
395 copyData->setOverrideIndex(-1);
396
397 cache->stringCache.insert(it.key(), std::make_pair(index, copyData));
398 }
399
400 cache->allowedRevisionCache = allowedRevisionCache;
401 cache->_dynamicStringData = _dynamicStringData;
402 cache->_dynamicClassName = _dynamicClassName;
403 cache->_defaultPropertyName = _defaultPropertyName;
404 cache->_listPropertyAssignBehavior = _listPropertyAssignBehavior;
405 return cache;
406}
407
408QQmlPropertyCache::Ptr QQmlPropertyCache::copyAndReserve(
409 int propertyCount, int methodCount, int signalCount, int enumCount) const
410{
411 QQmlPropertyCache::Ptr rv = copy(
412 QQmlMetaObjectPointer(), propertyCount + methodCount + signalCount);
413 rv->propertyIndexCache.reserve(propertyCount);
414 rv->methodIndexCache.reserve(methodCount);
415 rv->signalHandlerIndexCache.reserve(signalCount);
416 rv->enumCache.reserve(enumCount);
417 return rv;
418}
419
420QQmlPropertyCache::AppendResult
421QQmlPropertyCache::appendAlias(const QString &name, QQmlPropertyData::Flags flags, int coreIndex,
422 QMetaType propType, QTypeRevision version, int notifyIndex,
423 int encodedTargetIndex, int targetObjectId)
424{
425 QQmlPropertyData data;
426 data.setPropType(propType);
427 data.setCoreIndex(coreIndex);
428 data.setNotifyIndex(notifyIndex);
429 flags.setIsAlias(true);
430 data.setFlags(flags);
431 data.setAliasTarget(encodedTargetIndex);
432 data.setAliasTargetObjectId(targetObjectId);
433 data.setTypeVersion(version);
434
435 return appendPropertyAttr(name, std::move(data));
436}
437
438QQmlPropertyCache::AppendResult
439QQmlPropertyCache::appendComponentWrapper(int coreIndex, int wrappedObjectIndex)
440{
441 QQmlPropertyData data;
442 data.setCoreIndex(coreIndex);
443 QQmlPropertyData::Flags flags;
444 flags.setType(QQmlPropertyData::Flags::ComponentWrapperType);
445 data.setFlags(flags);
446 data.setWrappedObjectIndex(wrappedObjectIndex);
447
448 // Use a sentinel name so that defaultProperty() can find the wrapper.
449 // NB: We're not actually using the default property as default property. We only
450 // need some property to hold the wrapped object index.
451 _defaultPropertyName = QStringLiteral(".qt_component_wrapper__");
452 return appendPropertyAttr(_defaultPropertyName, std::move(data));
453}
454
455void QQmlPropertyCache::appendSignal(const QString &name, QQmlPropertyData::Flags flags,
456 int coreIndex, const QMetaType *types,
457 const QList<QByteArray> &names)
458{
459 QQmlPropertyData data;
460 data.setPropType(QMetaType());
461 data.setCoreIndex(coreIndex);
462 data.setFlags(flags);
463 data.setArguments(nullptr);
464
465 QQmlPropertyData handler = data;
466 handler.m_flags.setIsSignalHandler(true);
467
468 if (types) {
469 const auto argumentCount = names.size();
470 QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
471 new (args->types) QMetaType; // Invalid return type
472 ::memcpy(args->types + 1, types, argumentCount * sizeof(QMetaType));
473 data.setArguments(args);
474 }
475
476 QQmlPropertyData *old = findNamedProperty(name);
477 const auto overrideStatus = _handleOverride(data, old, OverrideSemantics::CheckMode::Minimal);
478 maybeLog(overrideStatus, name);
479 // remove assert when checkMode is expanded and adjust handling correspondingly. For now it
480 // verifies that some code-path work in the same way as before introduction of virtual and
481 // override keywords
482 Q_ASSERT(overrideStatus == OverrideSemantics::Status::NoOverride
483 || overrideStatus == OverrideSemantics::Status::Valid
484 || overrideStatus == OverrideSemantics::Status::OverridingFinal);
485 if (overrideStatus == OverrideSemantics::Status::OverridingFinal) {
486 // TODO QTBUG-141728
487 // Insert the overridden member and its signal once more, to keep the counts in sync
488 methodIndexCache.append(*old);
489 handler = *old;
490 handler.m_flags.setIsSignalHandler(true);
491 signalHandlerIndexCache.append(handler);
492 return;
493 }
494
495 int methodIndex = methodIndexCache.size();
496 methodIndexCache.append(data);
497
498 int signalHandlerIndex = signalHandlerIndexCache.size();
499 signalHandlerIndexCache.append(handler);
500
501 const QString handlerName = QQmlSignalNames::signalNameToHandlerName(name);
502
503 setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex);
504 setNamedProperty(handlerName, signalHandlerIndex + signalOffset(),
505 signalHandlerIndexCache.data() + signalHandlerIndex);
506}
507
508void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flags flags,
509 int coreIndex, QMetaType returnType,
510 const QList<QByteArray> &names,
511 const QList<QMetaType> &parameterTypes)
512{
513 int argumentCount = names.size();
514
515 QQmlPropertyData data;
516 data.setPropType(returnType);
517 data.setCoreIndex(coreIndex);
518 data.setFlags(flags);
519 QQmlPropertyData *old = findNamedProperty(name);
520 const auto overrideStatus = _handleOverride(data, old, OverrideSemantics::CheckMode::Minimal);
521 maybeLog(overrideStatus, name);
522 // remove assert when checkMode is expanded and adjust handling correspondingly. For now it
523 // verifies that some code-path work in the same way as before introduction of virtual and
524 // override keywords
525 Q_ASSERT(overrideStatus == OverrideSemantics::Status::NoOverride
526 || overrideStatus == OverrideSemantics::Status::Valid
527 || overrideStatus == OverrideSemantics::Status::OverridingFinal);
528 if (overrideStatus == OverrideSemantics::Status::OverridingFinal) {
529 // TODO QTBUG-141728
530 // Insert the overridden member once more, to keep the counts in sync
531 methodIndexCache.append(*old);
532 return;
533 }
534
535 QQmlPropertyCacheMethodArguments *args = createArgumentsObject(argumentCount, names);
536 new (args->types) QMetaType(returnType);
537 for (int ii = 0; ii < argumentCount; ++ii)
538 new (args->types + ii + 1) QMetaType(parameterTypes.at(ii));
539 data.setArguments(args);
540
541 int methodIndex = methodIndexCache.size();
542 methodIndexCache.append(data);
543
544 setNamedProperty(name, methodIndex + methodOffset(), methodIndexCache.data() + methodIndex);
545}
546
547void QQmlPropertyCache::appendEnum(const QString &name, const QList<QQmlEnumValue> &values)
548{
549 QQmlEnumData data;
550 data.name = name;
551 data.values = values;
552 enumCache.append(data);
553}
554
555// Returns this property cache's metaObject, creating it if necessary.
556const QMetaObject *QQmlPropertyCache::createMetaObject() const
557{
558 if (_metaObject.isNull()) {
559 QMetaObjectBuilder builder;
560 toMetaObjectBuilder(builder);
561 builder.setSuperClass(_parent->createMetaObject());
562 _metaObject.setSharedOnce(builder.toMetaObject());
563 }
564
565 return _metaObject.metaObject();
566}
567
568const QQmlPropertyData *QQmlPropertyCache::maybeUnresolvedProperty(int index) const
569{
570 if (index < 0 || index >= propertyCount())
571 return nullptr;
572
573 const QQmlPropertyData *rv = nullptr;
574 if (index < propertyIndexCacheStart)
575 return _parent->maybeUnresolvedProperty(index);
576 else
577 rv = const_cast<const QQmlPropertyData *>(&propertyIndexCache.at(index - propertyIndexCacheStart));
578 return rv;
579}
580
581const QQmlPropertyData *QQmlPropertyCache::defaultProperty() const
582{
583 return property(defaultPropertyName(), nullptr, nullptr);
584}
585
586void QQmlPropertyCache::setParent(QQmlPropertyCache::ConstPtr newParent)
587{
588 if (_parent != newParent)
589 _parent = std::move(newParent);
590}
591
592QQmlPropertyCache::Ptr
593QQmlPropertyCache::copyAndAppend(const QMetaObject *metaObject,
594 QTypeRevision typeVersion,
595 QQmlPropertyData::Flags propertyFlags,
596 QQmlPropertyData::Flags methodFlags,
597 QQmlPropertyData::Flags signalFlags) const
598{
599 Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
600
601 // Reserve enough space in the name hash for all the methods (including signals), all the
602 // signal handlers and all the properties. This assumes no name clashes, but this is the
603 // common case.
604 QQmlPropertyCache::Ptr rv = copy(
605 metaObject,
606 QMetaObjectPrivate::get(metaObject)->methodCount
607 + QMetaObjectPrivate::get(metaObject)->signalCount
608 + QMetaObjectPrivate::get(metaObject)->propertyCount);
609
610 rv->append(metaObject, typeVersion, propertyFlags, methodFlags, signalFlags);
611
612 return rv;
613}
614
615static QHashedString signalNameToHandlerName(const QHashedString &methodName)
616{
617 return QQmlSignalNames::signalNameToHandlerName(methodName);
618}
619
620static QHashedString signalNameToHandlerName(const QHashedCStringRef &methodName)
621{
622 return QQmlSignalNames::signalNameToHandlerName(
623 QLatin1StringView{ methodName.constData(), methodName.length() });
624}
625
626static inline std::pair<bool, int> deriveEncodingAndLength(const char *str)
627{
628 char utf8 = 0;
629 const char *cptr = str;
630 while (*cptr != 0) {
631 utf8 |= *cptr & 0x80;
632 ++cptr;
633 }
634 return std::make_pair(utf8, cptr - str);
635}
636
637void QQmlPropertyCache::append(const QMetaObject *metaObject,
638 QTypeRevision typeVersion,
639 QQmlPropertyData::Flags propertyFlags,
640 QQmlPropertyData::Flags methodFlags,
641 QQmlPropertyData::Flags signalFlags)
642{
643 allowedRevisionCache.append(QTypeRevision::zero());
644
645 int methodCount = metaObject->methodCount();
646 Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
647 int signalCount = metaObjectSignalCount(metaObject);
648 int classInfoCount = QMetaObjectPrivate::get(metaObject)->classInfoCount;
649
650 if (classInfoCount) {
651 int classInfoOffset = metaObject->classInfoOffset();
652 for (int ii = 0; ii < classInfoCount; ++ii) {
653 int idx = ii + classInfoOffset;
654 QMetaClassInfo mci = metaObject->classInfo(idx);
655 const char *name = mci.name();
656 if (0 == qstrcmp(name, "DefaultProperty")) {
657 _defaultPropertyName = QString::fromUtf8(mci.value());
658 } else if (0 == qstrcmp(name, "qt_QmlJSWrapperFactoryMethod")) {
659 const char * const factoryMethod = mci.value();
660 _jsFactoryMethodIndex = metaObject->indexOfSlot(factoryMethod);
661 if (_jsFactoryMethodIndex != -1)
662 _jsFactoryMethodIndex -= metaObject->methodOffset();
663 } else if (0 == qstrcmp(name, "QML.ListPropertyAssignBehavior")) {
664 _listPropertyAssignBehavior = mci.value();
665 }
666 }
667 }
668
669 //Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
670 static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
671 static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
672 static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
673 // These indices don't apply to gadgets, so don't block them.
674 // It is enough to check for QObject::staticMetaObject here because the loop below excludes
675 // methods of parent classes: It starts at metaObject->methodOffset()
676 const bool preventDestruction = (metaObject == &QObject::staticMetaObject);
677
678 int methodOffset = metaObject->methodOffset();
679 int signalOffset = signalCount - QMetaObjectPrivate::get(metaObject)->signalCount;
680
681 // update() should have reserved enough space in the vector that this doesn't cause a realloc
682 // and invalidate the stringCache.
683 methodIndexCache.resize(methodCount - methodIndexCacheStart);
684 signalHandlerIndexCache.resize(signalCount - signalHandlerIndexCacheStart);
685 int signalHandlerIndex = signalOffset;
686 for (int ii = methodOffset; ii < methodCount; ++ii) {
687 if (preventDestruction && (ii == destroyedIdx1 || ii == destroyedIdx2 || ii == deleteLaterIdx))
688 continue;
689 const QMetaMethod &m = metaObject->method(ii);
690 if (m.access() == QMetaMethod::Private)
691 continue;
692
693 // Extract method name
694 // It's safe to keep the raw name pointer
695 Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 7);
696
697 QQmlPropertyData *data = &methodIndexCache[ii - methodIndexCacheStart];
698 QQmlPropertyData *sigdata = nullptr;
699
700 if (m.methodType() == QMetaMethod::Signal)
701 data->setFlags(signalFlags);
702 else
703 data->setFlags(methodFlags);
704
705 data->load(m);
706
707 Q_ASSERT((allowedRevisionCache.size() - 1) < Q_INT16_MAX);
708 data->setMetaObjectOffset(allowedRevisionCache.size() - 1);
709
710 if (data->isSignal()) {
711 sigdata = &signalHandlerIndexCache[signalHandlerIndex - signalHandlerIndexCacheStart];
712 *sigdata = *data;
713 sigdata->m_flags.setIsSignalHandler(true);
714 }
715
716 const auto doSetNamedProperty = [&](const auto &methodName) {
717 QQmlPropertyData *old = nullptr;
718 if (StringCache::mapped_type *it = stringCache.value(methodName)) {
719 const auto overrideStatus = _handleOverride(*data, (old = it->second),
720 OverrideSemantics::CheckMode::Minimal);
721 maybeLog(overrideStatus, methodName);
722 // remove assert when checkMode is expanded and adjust handling correspondingly. For
723 // now it verifies that some code-path work in the same way as before introduction
724 // of virtual and override keywords
725 Q_ASSERT(overrideStatus == OverrideSemantics::Status::NoOverride
726 || overrideStatus == OverrideSemantics::Status::Valid
727 || overrideStatus == OverrideSemantics::Status::OverridingFinal);
728 if (overrideStatus == OverrideSemantics::Status::OverridingFinal) {
729 // TODO QTBUG-141728
730 *data = *old;
731 if (sigdata) {
732 // Keep the signal counts in sync,
733 // even if the "old" data has no real signal.
734 *sigdata = *old;
735 sigdata->m_flags.setIsSignalHandler(true);
736 ++signalHandlerIndex;
737 }
738 return;
739 }
740 }
741
742 setNamedProperty(methodName, ii, data);
743
744 if (data->isSignal()) {
745
746 // TODO: Remove this once we can. Signals should not be overridable.
747 if constexpr (std::is_same_v<std::decay_t<decltype(methodName)>, QHashedCStringRef>)
748 data->m_flags.setIsOverridableSignal(true);
749
750 setNamedProperty(signalNameToHandlerName(methodName), ii, sigdata);
751 ++signalHandlerIndex;
752 }
753 };
754
755 const char *str = m.nameView().constData();
756 const auto [isUtf8, len] = deriveEncodingAndLength(str);
757 if (isUtf8)
758 doSetNamedProperty(QHashedString(QString::fromUtf8(str, len)));
759 else
760 doSetNamedProperty(QHashedCStringRef(str, len));
761 }
762
763 int propCount = metaObject->propertyCount();
764 int propOffset = metaObject->propertyOffset();
765
766 // update() should have reserved enough space in the vector that this doesn't cause a realloc
767 // and invalidate the stringCache.
768 propertyIndexCache.resize(propCount - propertyIndexCacheStart);
769 for (int ii = propOffset; ii < propCount; ++ii) {
770 QMetaProperty p = metaObject->property(ii);
771 if (!p.isScriptable())
772 continue;
773
774 // TODO QTBUG-141728
775 QQmlPropertyData *data = &propertyIndexCache[ii - propertyIndexCacheStart];
776
777 data->setFlags(propertyFlags);
778 data->load(p);
779 data->setTypeVersion(typeVersion);
780
781 Q_ASSERT((allowedRevisionCache.size() - 1) < Q_INT16_MAX);
782 data->setMetaObjectOffset(allowedRevisionCache.size() - 1);
783
784 const auto doSetNamedProperty = [this](const auto &propName, int index, auto *propData) {
785 QQmlPropertyData *existingPropData = findNamedProperty(propName);
786 const auto overrideStatus = _handleOverride(*propData, existingPropData,
787 OverrideSemantics::CheckMode::Full);
788 maybeLog(overrideStatus, propName);
789 if (!OverrideSemantics::isValidOverride(overrideStatus)) {
790 if (existingPropData) {
791 // TODO QTBUG-141728
792 *propData = *existingPropData;
793 }
794 return;
795 }
796
797 setNamedProperty(propName, index, propData);
798 };
799
800 const char *str = p.name();
801 const auto [isUtf8, len] = deriveEncodingAndLength(str);
802 if (isUtf8)
803 doSetNamedProperty(QHashedString(QString::fromUtf8(str, len)), ii, data);
804 else
805 doSetNamedProperty(QHashedCStringRef(str, len), ii, data);
806
807 bool isGadget = true;
808 for (const QMetaObject *it = metaObject; it != nullptr; it = it->superClass()) {
809 if (it == &QObject::staticMetaObject)
810 isGadget = false;
811 }
812
813 // otherwise always dispatch over a 'normal' meta-call so the QQmlValueType can intercept
814 if (!isGadget && !data->isAlias())
815 data->trySetStaticMetaCallFunction(metaObject->d.static_metacall, ii - propOffset);
816 }
817}
818
819void QQmlPropertyCache::update(const QMetaObject *metaObject)
820{
821 Q_ASSERT(metaObject);
822 stringCache.clear();
823
824 // Preallocate enough space in the index caches for all the properties/methods/signals that
825 // are not cached in a parent cache so that the caches never need to be reallocated as this
826 // would invalidate pointers stored in the stringCache.
827 int pc = metaObject->propertyCount();
828 int mc = metaObject->methodCount();
829 int sc = metaObjectSignalCount(metaObject);
830 propertyIndexCache.reserve(pc - propertyIndexCacheStart);
831 methodIndexCache.reserve(mc - methodIndexCacheStart);
832 signalHandlerIndexCache.reserve(sc - signalHandlerIndexCacheStart);
833
834 // Reserve enough space in the stringCache for all properties/methods/signals including those
835 // cached in a parent cache.
836 stringCache.reserve(pc + mc + sc);
837
838 if (metaObject)
839 append(metaObject, QTypeRevision());
840}
841
842/*! \internal
843 invalidates and updates the PropertyCache if the QMetaObject has changed.
844 This function is used in the tooling to update dynamic properties.
845*/
846void QQmlPropertyCache::invalidate(const QMetaObject *metaObject)
847{
848 propertyIndexCache.clear();
849 methodIndexCache.clear();
850 signalHandlerIndexCache.clear();
851
852 argumentsCache = nullptr;
853
854 int pc = metaObject->propertyCount();
855 int mc = metaObject->methodCount();
856 int sc = metaObjectSignalCount(metaObject);
857 int reserve = pc + mc + sc;
858
859 if (parent()) {
860 propertyIndexCacheStart = parent()->propertyIndexCache.size() + parent()->propertyIndexCacheStart;
861 methodIndexCacheStart = parent()->methodIndexCache.size() + parent()->methodIndexCacheStart;
862 signalHandlerIndexCacheStart = parent()->signalHandlerIndexCache.size() + parent()->signalHandlerIndexCacheStart;
863 stringCache.linkAndReserve(parent()->stringCache, reserve);
864 append(metaObject, QTypeRevision());
865 } else {
866 propertyIndexCacheStart = 0;
867 methodIndexCacheStart = 0;
868 signalHandlerIndexCacheStart = 0;
869 update(metaObject);
870 }
871}
872
873const QQmlPropertyData *QQmlPropertyCache::findProperty(
874 StringCache::ConstIterator it, QObject *object,
875 const QQmlRefPointer<QQmlContextData> &context) const
876{
877 QQmlData *data = (object ? QQmlData::get(object) : nullptr);
878 const QQmlVMEMetaObject *vmemo = nullptr;
879 if (data && data->hasVMEMetaObject) {
880 QObjectPrivate *op = QObjectPrivate::get(object);
881 vmemo = static_cast<const QQmlVMEMetaObject *>(op->metaObject);
882 }
883 return findProperty(it, vmemo, context);
884}
885
886namespace {
887
888inline bool contextHasNoExtensions(const QQmlRefPointer<QQmlContextData> &context)
889{
890 // This context has no extension if its parent is the engine's rootContext,
891 // which has children but no imports
892 const QQmlRefPointer<QQmlContextData> parent = context->parent();
893 return (!parent || !parent->imports());
894}
895
896inline int maximumIndexForProperty(const QQmlPropertyData *prop, const int methodCount, const int signalCount, const int propertyCount)
897{
898 return prop->isFunction() ? methodCount
899 : prop->isSignalHandler() ? signalCount
900 : propertyCount;
901}
902
903}
904
905const QQmlPropertyData *QQmlPropertyCache::findProperty(
906 StringCache::ConstIterator it, const QQmlVMEMetaObject *vmemo,
907 const QQmlRefPointer<QQmlContextData> &context) const
908{
909 StringCache::ConstIterator end = stringCache.end();
910
911 if (it != end) {
912 const QQmlPropertyData *result = it.value().second;
913
914 // If there exists a typed property (not a function or signal handler), of the
915 // right name available to the specified context, we need to return that
916 // property rather than any subsequent override
917
918 if (vmemo && context && !contextHasNoExtensions(context)) {
919 // Find the meta-object that corresponds to the supplied context
920 do {
921 if (vmemo->contextData() == context)
922 break;
923
924 vmemo = vmemo->parentVMEMetaObject();
925 } while (vmemo);
926 }
927
928 if (vmemo) {
929 const int methodCount = vmemo->cache->methodCount();
930 const int signalCount = vmemo->cache->signalCount();
931 const int propertyCount = vmemo->cache->propertyCount();
932
933 // Ensure that the property we resolve to is accessible from this meta-object
934 do {
935 const StringCache::mapped_type &property(it.value());
936
937 if (property.first < maximumIndexForProperty(property.second, methodCount, signalCount, propertyCount)) {
938 // This property is available in the specified context
939 if (property.second->isFunction() || property.second->isSignalHandler()) {
940 // Prefer the earlier resolution
941 } else {
942 // Prefer the typed property to any previous property found
943 result = property.second;
944 }
945 break;
946 }
947
948 // See if there is a better candidate
949 it = stringCache.findNext(it);
950 } while (it != end);
951 }
952
953 return result;
954 }
955
956 return nullptr;
957}
958
959// Note, this function is called when adding aliases, hence data.isEnum() can possibly be true
960QQmlPropertyCache::AppendResult QQmlPropertyCache::appendPropertyAttr(const QString &name,
961 QQmlPropertyData &&data)
962{
963 QQmlPropertyData *old = findNamedProperty(name);
964 const auto overrideStatus = _handleOverride(data, old, OverrideSemantics::CheckMode::Full);
965 maybeLog(overrideStatus, name);
966 if (!OverrideSemantics::isValidOverride(overrideStatus)) {
967 // TODO QTBUG-141728
968 // Insert the overridden member once more, to keep the counts in sync
969 propertyIndexCache.append(old ? *old : data);
970 return q23::make_unexpected(overrideStatus);
971 }
972
973 const int index = propertyIndexCache.size();
974 propertyIndexCache.append(std::move(data));
975
976 setNamedProperty(name, index + propertyOffset(), propertyIndexCache.data() + index);
977 return {};
978}
979
980void QQmlPropertyData::markAsOverrideOf(QQmlPropertyData *predecessor)
981{
982 Q_ASSERT(predecessor != this);
983
984 if (!predecessor) {
985 return;
986 }
987
988 setOverrideIndexIsProperty(!predecessor->isFunction());
989 setOverrideIndex(predecessor->coreIndex());
990 // propagate "virtuality"
991 m_flags.setIsVirtual(predecessor->isVirtual());
992 predecessor->m_flags.setIsOverridden(true);
993 Q_ASSERT(predecessor->isOverridden());
994 return;
995}
996
997QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(
998 int argc, const QList<QByteArray> &names)
999{
1000 typedef QQmlPropertyCacheMethodArguments A;
1001 A *args = static_cast<A *>(malloc(sizeof(A) + argc * sizeof(QMetaType)));
1002 args->names = argc ? new QList<QByteArray>(names) : nullptr;
1003 args->next = argumentsCache;
1004 argumentsCache = args;
1005 return args;
1006}
1007
1008QString QQmlPropertyCache::signalParameterStringForJS(
1009 const QList<QByteArray> &parameterNameList, QString *errorString)
1010{
1011 bool unnamedParameter = false;
1012 QString parameters;
1013
1014 const qsizetype count = parameterNameList.size();
1015 if (count > std::numeric_limits<quint16>::max())
1016 *errorString = QCoreApplication::translate("QQmlRewrite", "Signal has an excessive number of parameters: %1").arg(count);
1017
1018 for (qsizetype i = 0; i < count; ++i) {
1019 if (i > 0)
1020 parameters += QLatin1Char(',');
1021 const QByteArray &param = parameterNameList.at(i);
1022 if (param.isEmpty()) {
1023 unnamedParameter = true;
1024 } else if (unnamedParameter) {
1025 if (errorString)
1026 *errorString = QCoreApplication::translate("QQmlRewrite", "Signal uses unnamed parameter followed by named parameter.");
1027 return QString();
1028 } else if (QV4::Compiler::Codegen::isNameGlobal(param)) {
1029 if (errorString)
1030 *errorString = QCoreApplication::translate("QQmlRewrite", "Signal parameter \"%1\" hides global variable.").arg(QString::fromUtf8(param));
1031 return QString();
1032 }
1033 parameters += QString::fromUtf8(param);
1034 }
1035
1036 return parameters;
1037}
1038
1039int QQmlPropertyCache::originalClone(int index) const
1040{
1041 while (signal(index)->isCloned())
1042 --index;
1043 return index;
1044}
1045
1046int QQmlPropertyCache::originalClone(const QObject *object, int index)
1047{
1048 QQmlData *data = QQmlData::get(object);
1049 if (data && data->propertyCache) {
1050 const QQmlPropertyCache *cache = data->propertyCache.data();
1051 const QQmlPropertyData *sig = cache->signal(index);
1052 while (sig && sig->isCloned()) {
1053 --index;
1054 sig = cache->signal(index);
1055 }
1056 } else {
1057 while (QMetaObjectPrivate::signal(object->metaObject(), index).attributes() & QMetaMethod::Cloned)
1058 --index;
1059 }
1060 return index;
1061}
1062
1063template<typename T>
1064static QQmlPropertyData qQmlPropertyCacheCreate(const QMetaObject *metaObject, const T& propertyName)
1065{
1066 Q_ASSERT(metaObject);
1067 static_assert(std::is_same_v<T, QByteArray>);
1068
1069 QQmlPropertyData rv;
1070
1071 /* It's important to check the method list before checking for properties;
1072 * otherwise, if the meta object is dynamic, a property will be created even
1073 * if not found and it might obscure a method having the same name. */
1074
1075 //Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
1076 static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
1077 static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
1078 static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
1079 // These indices don't apply to gadgets, so don't block them.
1080 const bool preventDestruction = metaObject->superClass() || metaObject == &QObject::staticMetaObject;
1081
1082 int methodCount = metaObject->methodCount();
1083 for (int ii = methodCount - 1; ii >= 0; --ii) {
1084 if (preventDestruction && (ii == destroyedIdx1 || ii == destroyedIdx2 || ii == deleteLaterIdx))
1085 continue;
1086 QMetaMethod m = metaObject->method(ii);
1087 if (m.access() == QMetaMethod::Private)
1088 continue;
1089
1090 if (m.name() == propertyName) {
1091 rv.load(m);
1092 return rv;
1093 }
1094 }
1095
1096 {
1097 const QMetaObject *cmo = metaObject;
1098 while (cmo) {
1099 int idx = cmo->indexOfProperty(propertyName.data());
1100 if (idx != -1) {
1101 QMetaProperty p = cmo->property(idx);
1102 if (p.isScriptable()) {
1103 rv.load(p);
1104 return rv;
1105 } else {
1106 bool changed = false;
1107 while (cmo && cmo->propertyOffset() >= idx) {
1108 cmo = cmo->superClass();
1109 changed = true;
1110 }
1111 /* If the "cmo" variable didn't change, set it to 0 to
1112 * avoid running into an infinite loop */
1113 if (!changed) cmo = nullptr;
1114 }
1115 } else {
1116 cmo = nullptr;
1117 }
1118 }
1119 }
1120 return rv;
1121}
1122
1123static inline QByteArray qQmlPropertyCacheToString(QLatin1String string)
1124{
1125 return string.toUtf8();
1126}
1127
1128static inline QByteArray qQmlPropertyCacheToString(QStringView string)
1129{
1130 return string.toUtf8();
1131}
1132
1133static inline QByteArray qQmlPropertyCacheToString(const QV4::String *string)
1134{
1135 return string->toQString().toUtf8();
1136}
1137
1138template<typename T>
1139const QQmlPropertyData *
1140qQmlPropertyCacheProperty(QObject *obj, T name, const QQmlRefPointer<QQmlContextData> &context,
1141 QQmlPropertyData *local)
1142{
1143 const QQmlPropertyCache *cache = nullptr;
1144
1145 QQmlData *ddata = QQmlData::get(obj, false);
1146
1147 if (ddata && ddata->propertyCache) {
1148 cache = ddata->propertyCache.data();
1149 } else if (auto newCache = QQmlMetaType::propertyCache(obj)) {
1150 cache = newCache.data();
1151 ddata = QQmlData::get(obj, true);
1152 ddata->propertyCache = std::move(newCache);
1153 }
1154
1155 const QQmlPropertyData *rv = nullptr;
1156
1157 if (cache) {
1158 rv = cache->property(name, obj, context);
1159 } else if (local) {
1160 *local = qQmlPropertyCacheCreate(obj->metaObject(), qQmlPropertyCacheToString(name));
1161 if (local->isValid())
1162 rv = local;
1163 }
1164
1165 return rv;
1166}
1167
1168const QQmlPropertyData *QQmlPropertyCache::property(
1169 QObject *obj, const QV4::String *name, const QQmlRefPointer<QQmlContextData> &context,
1170 QQmlPropertyData *local)
1171{
1172 return qQmlPropertyCacheProperty<const QV4::String *>(obj, name, context, local);
1173}
1174
1175const QQmlPropertyData *QQmlPropertyCache::property(
1176 QObject *obj, QStringView name, const QQmlRefPointer<QQmlContextData> &context,
1177 QQmlPropertyData *local)
1178{
1179 return qQmlPropertyCacheProperty<const QStringView &>(obj, name, context, local);
1180}
1181
1182const QQmlPropertyData *QQmlPropertyCache::property(
1183 QObject *obj, const QLatin1String &name, const QQmlRefPointer<QQmlContextData> &context,
1184 QQmlPropertyData *local)
1185{
1186 return qQmlPropertyCacheProperty<const QLatin1String &>(obj, name, context, local);
1187}
1188
1189// this function is copied from qmetaobject.cpp
1190static inline const QByteArray stringData(const QMetaObject *mo, int index)
1191{
1192 uint offset = mo->d.stringdata[2*index];
1193 uint length = mo->d.stringdata[2*index + 1];
1194 const char *string = reinterpret_cast<const char *>(mo->d.stringdata) + offset;
1195 return QByteArray::fromRawData(string, length);
1196}
1197
1198const char *QQmlPropertyCache::className() const
1199{
1200 if (const QMetaObject *mo = _metaObject.metaObject())
1201 return mo->className();
1202 else
1203 return _dynamicClassName.constData();
1204}
1205
1206void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder) const
1207{
1208 struct Sort { static bool lt(const std::pair<QString, const QQmlPropertyData *> &lhs,
1209 const std::pair<QString, const QQmlPropertyData *> &rhs) {
1210 return lhs.second->coreIndex() < rhs.second->coreIndex();
1211 } };
1212
1213 struct Insert { static void in(const QQmlPropertyCache *This,
1214 QList<std::pair<QString, const QQmlPropertyData *> > &properties,
1215 QList<std::pair<QString, const QQmlPropertyData *> > &methods,
1216 StringCache::ConstIterator iter, const QQmlPropertyData *data) {
1217 if (data->isSignalHandler())
1218 return;
1219
1220 if (data->isFunction()) {
1221 if (data->coreIndex() < This->methodIndexCacheStart)
1222 return;
1223
1224 std::pair<QString, const QQmlPropertyData *> entry = std::make_pair((QString)iter.key(), data);
1225 // Overrides can cause the entry to already exist
1226 if (!methods.contains(entry)) methods.append(entry);
1227
1228 data = This->overrideData(data);
1229 if (data && !data->isFunction()) Insert::in(This, properties, methods, iter, data);
1230 } else {
1231 if (data->coreIndex() < This->propertyIndexCacheStart)
1232 return;
1233
1234 std::pair<QString, const QQmlPropertyData *> entry = std::make_pair((QString)iter.key(), data);
1235 // Overrides can cause the entry to already exist
1236 if (!properties.contains(entry)) properties.append(entry);
1237
1238 data = This->overrideData(data);
1239 if (data) Insert::in(This, properties, methods, iter, data);
1240 }
1241
1242 } };
1243
1244 builder.setClassName(_dynamicClassName);
1245
1246 QList<std::pair<QString, const QQmlPropertyData *> > properties;
1247 QList<std::pair<QString, const QQmlPropertyData *> > methods;
1248
1249 for (StringCache::ConstIterator iter = stringCache.begin(), cend = stringCache.end(); iter != cend; ++iter)
1250 Insert::in(this, properties, methods, iter, iter.value().second);
1251
1252 // Any invalid overrides are not linked by name into the properties and methods hashes.
1253 // Therefore there can be more properties and methods than present in the hashes.
1254 Q_ASSERT(properties.size() <= propertyIndexCache.size());
1255 Q_ASSERT(methods.size() <= methodIndexCache.size());
1256
1257 std::sort(properties.begin(), properties.end(), Sort::lt);
1258 std::sort(methods.begin(), methods.end(), Sort::lt);
1259
1260 for (int ii = 0; ii < properties.size(); ++ii) {
1261 const QQmlPropertyData *data = properties.at(ii).second;
1262
1263 int notifierId = -1;
1264 if (data->notifyIndex() != -1)
1265 notifierId = data->notifyIndex() - signalHandlerIndexCacheStart;
1266
1267 QMetaPropertyBuilder property = builder.addProperty(properties.at(ii).first.toUtf8(),
1268 data->propType().name(),
1269 data->propType(),
1270 notifierId);
1271
1272 property.setReadable(true);
1273 property.setWritable(data->isWritable());
1274 property.setResettable(data->isResettable());
1275 property.setBindable(data->notifiesViaBindable());
1276 property.setAlias(data->isAlias());
1277 }
1278
1279 for (int ii = 0; ii < methods.size(); ++ii) {
1280 const QQmlPropertyData *data = methods.at(ii).second;
1281
1282 QByteArray returnType;
1283 if (data->propType().isValid())
1284 returnType = data->propType().name();
1285
1286 QByteArray signature;
1287 // '+=' reserves extra capacity. Follow-up appending will be probably free.
1288 signature += methods.at(ii).first.toUtf8() + '(';
1289
1290 QQmlPropertyCacheMethodArguments *arguments = nullptr;
1291 if (data->hasArguments()) {
1292 arguments = data->arguments();
1293 for (int ii = 0, end = arguments->names ? arguments->names->size() : 0;
1294 ii < end; ++ii) {
1295 if (ii != 0)
1296 signature.append(',');
1297 signature.append(arguments->types[1 + ii].name());
1298 }
1299 }
1300
1301 signature.append(')');
1302
1303 QMetaMethodBuilder method;
1304 if (data->isSignal()) {
1305 method = builder.addSignal(signature);
1306 } else {
1307 method = builder.addSlot(signature);
1308 }
1309 method.setAccess(QMetaMethod::Public);
1310
1311 if (arguments && arguments->names)
1312 method.setParameterNames(*arguments->names);
1313
1314 if (!returnType.isEmpty())
1315 method.setReturnType(returnType);
1316 }
1317
1318 for (int ii = 0; ii < enumCache.size(); ++ii) {
1319 const QQmlEnumData &enumData = enumCache.at(ii);
1320 QMetaEnumBuilder enumeration = builder.addEnumerator(enumData.name.toUtf8());
1321 enumeration.setIsScoped(true);
1322 for (int jj = 0; jj < enumData.values.size(); ++jj) {
1323 const QQmlEnumValue &value = enumData.values.at(jj);
1324 enumeration.addKey(value.namedValue.toUtf8(), value.value);
1325 }
1326 }
1327
1328 if (!_defaultPropertyName.isEmpty()) {
1329 const QQmlPropertyData *dp = property(_defaultPropertyName, nullptr, nullptr);
1330 if (dp && dp->coreIndex() >= propertyIndexCacheStart) {
1331 Q_ASSERT(!dp->isFunction());
1332 builder.addClassInfo("DefaultProperty", _defaultPropertyName.toUtf8());
1333 }
1334 }
1335
1336 if (!_listPropertyAssignBehavior.isEmpty())
1337 builder.addClassInfo("QML.ListPropertyAssignBehavior", _listPropertyAssignBehavior);
1338}
1339
1340namespace {
1341template <typename StringVisitor, typename TypeInfoVisitor>
1342int visitMethods(const QMetaObject &mo, int methodOffset, int methodCount,
1343 StringVisitor visitString, TypeInfoVisitor visitTypeInfo)
1344{
1345 int fieldsForParameterData = 0;
1346
1347 bool hasOldStyleRevisionedMethods = false;
1348
1349 for (int i = 0; i < methodCount; ++i) {
1350 const int handle = methodOffset + i * QMetaObjectPrivate::IntsPerMethod;
1351
1352 const uint flags = mo.d.data[handle + 4];
1353 if (flags & MethodRevisioned) {
1354 if (mo.d.data[0] < 13)
1355 hasOldStyleRevisionedMethods = true;
1356 else
1357 fieldsForParameterData += 1; // revision
1358 }
1359
1360 visitString(mo.d.data[handle + 0]); // name
1361 visitString(mo.d.data[handle + 3]); // tag
1362
1363 const int argc = mo.d.data[handle + 1];
1364 const int paramIndex = mo.d.data[handle + 2];
1365
1366 fieldsForParameterData += argc * 2; // type and name
1367 fieldsForParameterData += 1; // + return type
1368
1369 // return type + args
1370 for (int i = 0; i < 1 + argc; ++i) {
1371 // type name (maybe)
1372 visitTypeInfo(mo.d.data[paramIndex + i]);
1373
1374 // parameter name
1375 if (i > 0)
1376 visitString(mo.d.data[paramIndex + argc + i]);
1377 }
1378 }
1379
1380 int fieldsForRevisions = 0;
1381 if (hasOldStyleRevisionedMethods)
1382 fieldsForRevisions = methodCount;
1383
1384 return methodCount * QMetaObjectPrivate::IntsPerMethod
1385 + fieldsForRevisions + fieldsForParameterData;
1386}
1387
1388template <typename StringVisitor, typename TypeInfoVisitor>
1389int visitProperties(const QMetaObject &mo, StringVisitor visitString, TypeInfoVisitor visitTypeInfo)
1390{
1391 const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
1392
1393 for (int i = 0; i < priv->propertyCount; ++i) {
1394 const int handle = priv->propertyData + i * QMetaObjectPrivate::IntsPerProperty;
1395
1396 visitString(mo.d.data[handle]); // name
1397 visitTypeInfo(mo.d.data[handle + 1]);
1398 }
1399
1400 return priv->propertyCount * QMetaObjectPrivate::IntsPerProperty;
1401}
1402
1403template <typename StringVisitor>
1404int visitClassInfo(const QMetaObject &mo, StringVisitor visitString)
1405{
1406 const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
1407 const int intsPerClassInfo = 2;
1408
1409 for (int i = 0; i < priv->classInfoCount; ++i) {
1410 const int handle = priv->classInfoData + i * intsPerClassInfo;
1411
1412 visitString(mo.d.data[handle]); // key
1413 visitString(mo.d.data[handle + 1]); // value
1414 }
1415
1416 return priv->classInfoCount * intsPerClassInfo;
1417}
1418
1419template <typename StringVisitor>
1420int visitEnumerations(const QMetaObject &mo, StringVisitor visitString)
1421{
1422 const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
1423
1424 int fieldCount = priv->enumeratorCount * QMetaObjectPrivate::IntsPerEnum;
1425
1426 for (int i = 0; i < priv->enumeratorCount; ++i) {
1427 const uint *enumeratorData = mo.d.data + priv->enumeratorData + i * QMetaObjectPrivate::IntsPerEnum;
1428
1429 const uint keyCount = enumeratorData[3];
1430 fieldCount += keyCount * 2;
1431
1432 visitString(enumeratorData[0]); // name
1433 visitString(enumeratorData[1]); // enum name
1434
1435 const uint keyOffset = enumeratorData[4];
1436
1437 for (uint j = 0; j < keyCount; ++j) {
1438 visitString(mo.d.data[keyOffset + 2 * j]);
1439 }
1440 }
1441
1442 return fieldCount;
1443}
1444
1445template <typename StringVisitor>
1446int countMetaObjectFields(const QMetaObject &mo, StringVisitor stringVisitor)
1447{
1448 const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
1449
1450 const auto typeInfoVisitor = [&stringVisitor](uint typeInfo) {
1451 if (typeInfo & IsUnresolvedType)
1452 stringVisitor(typeInfo & TypeNameIndexMask);
1453 };
1454
1455 int fieldCount = MetaObjectPrivateFieldCount;
1456
1457 fieldCount += visitMethods(mo, priv->methodData, priv->methodCount, stringVisitor,
1458 typeInfoVisitor);
1459 fieldCount += visitMethods(mo, priv->constructorData, priv->constructorCount, stringVisitor,
1460 typeInfoVisitor);
1461
1462 fieldCount += visitProperties(mo, stringVisitor, typeInfoVisitor);
1463 fieldCount += visitClassInfo(mo, stringVisitor);
1464 fieldCount += visitEnumerations(mo, stringVisitor);
1465
1466 return fieldCount;
1467}
1468
1469} // anonymous namespace
1470
1471static_assert(QMetaObjectPrivate::OutputRevision == 13, "Check and adjust determineMetaObjectSizes");
1472
1473bool QQmlPropertyCache::determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount,
1474 int *stringCount)
1475{
1476 const QMetaObjectPrivate *priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
1477 if (priv->revision != QMetaObjectPrivate::OutputRevision)
1478 return false;
1479
1480 uint highestStringIndex = 0;
1481 const auto stringIndexVisitor = [&highestStringIndex](uint index) {
1482 highestStringIndex = qMax(highestStringIndex, index);
1483 };
1484
1485 *fieldCount = countMetaObjectFields(mo, stringIndexVisitor);
1486 *stringCount = highestStringIndex + 1;
1487
1488 return true;
1489}
1490
1491bool QQmlPropertyCache::addToHash(QCryptographicHash &hash, const QMetaObject &mo)
1492{
1493 int fieldCount = 0;
1494 int stringCount = 0;
1495 if (!determineMetaObjectSizes(mo, &fieldCount, &stringCount)) {
1496 return false;
1497 }
1498
1499 hash.addData({reinterpret_cast<const char *>(mo.d.data), qsizetype(fieldCount * sizeof(uint))});
1500 for (int i = 0; i < stringCount; ++i) {
1501 hash.addData(stringData(&mo, i));
1502 }
1503
1504 return true;
1505}
1506
1507QByteArray QQmlPropertyCache::checksum(QHash<quintptr, QByteArray> *checksums, bool *ok) const
1508{
1509 auto it = checksums->constFind(quintptr(this));
1510 if (it != checksums->constEnd()) {
1511 *ok = true;
1512 return *it;
1513 }
1514
1515 // Generate a checksum on the meta-object data only on C++ types.
1516 if (isComposite()) {
1517 *ok = false;
1518 return QByteArray();
1519 }
1520
1521 QCryptographicHash hash(QCryptographicHash::Md5);
1522
1523 if (_parent) {
1524 hash.addData(_parent->checksum(checksums, ok));
1525 if (!*ok)
1526 return QByteArray();
1527 }
1528
1529 if (!addToHash(hash, *_metaObject.metaObject())) {
1530 *ok = false;
1531 return QByteArray();
1532 }
1533
1534 const QByteArray result = hash.result();
1535 if (result.isEmpty()) {
1536 *ok = false;
1537 } else {
1538 *ok = true;
1539 checksums->insert(quintptr(this), result);
1540 }
1541 return result;
1542}
1543
1544/*! \internal
1545 \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
1546 This is different from QMetaMethod::methodIndex().
1547*/
1548QList<QByteArray> QQmlPropertyCache::signalParameterNames(int index) const
1549{
1550 const QQmlPropertyData *signalData = signal(index);
1551 if (signalData && signalData->hasArguments()) {
1552 QQmlPropertyCacheMethodArguments *args = (QQmlPropertyCacheMethodArguments *)signalData->arguments();
1553 if (args && args->names)
1554 return *args->names;
1555 const QMetaMethod &method = QMetaObjectPrivate::signal(firstCppMetaObject(), index);
1556 return method.parameterNames();
1557 }
1558 return QList<QByteArray>();
1559}
1560
1561QT_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 QByteArray 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)