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

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