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