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

source code of qtdeclarative/src/qml/qml/qqmlpropertycache.cpp