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

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