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