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 | |
28 | QT_BEGIN_NAMESPACE |
29 | |
30 | #define Q_INT16_MAX 32767 |
31 | |
32 | static 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 | |
40 | QQmlPropertyData::Flags |
41 | QQmlPropertyData::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 | |
72 | void 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 | |
83 | void 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 | */ |
118 | QQmlPropertyCache::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 | |
147 | QQmlPropertyCache::~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 | |
162 | QQmlPropertyCache::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 | |
178 | QQmlPropertyCache::Ptr QQmlPropertyCache::copy() const |
179 | { |
180 | return copy(mo: _metaObject, reserve: 0); |
181 | } |
182 | |
183 | QQmlPropertyCache::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 | */ |
200 | void 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 | |
221 | void 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 | |
260 | void QQmlPropertyCache::appendMethod(const QString &name, QQmlPropertyData::Flags flags, |
261 | int coreIndex, QMetaType returnType, |
262 | const QList<QByteArray> &names, |
263 | const QVector<QMetaType> ¶meterTypes) |
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 | |
287 | void 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. |
296 | const 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 | |
308 | const 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 | |
321 | const QQmlPropertyData *QQmlPropertyCache::defaultProperty() const |
322 | { |
323 | return property(key: defaultPropertyName(), object: nullptr, context: nullptr); |
324 | } |
325 | |
326 | void QQmlPropertyCache::setParent(QQmlPropertyCache::ConstPtr newParent) |
327 | { |
328 | if (_parent != newParent) |
329 | _parent = std::move(newParent); |
330 | } |
331 | |
332 | QQmlPropertyCache::Ptr |
333 | QQmlPropertyCache::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 | |
355 | void 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 | |
546 | void 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 | */ |
573 | void 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 | |
600 | const 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 | |
613 | namespace { |
614 | |
615 | inline 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 | |
623 | inline 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 | |
632 | const 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 | |
686 | QString QQmlPropertyData::name(QObject *object) const |
687 | { |
688 | if (!object) |
689 | return QString(); |
690 | |
691 | return name(object->metaObject()); |
692 | } |
693 | |
694 | QString 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 | |
709 | bool 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 | |
722 | QQmlPropertyCacheMethodArguments *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 | |
733 | QString QQmlPropertyCache::signalParameterStringForJS(QV4::ExecutionEngine *engine, const QList<QByteArray> ¶meterNameList, 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 ¶m = 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 | |
764 | int QQmlPropertyCache::originalClone(int index) const |
765 | { |
766 | while (signal(index)->isCloned()) |
767 | --index; |
768 | return index; |
769 | } |
770 | |
771 | int 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 | |
788 | template<typename T> |
789 | static 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 | |
847 | static inline const char *qQmlPropertyCacheToString(QLatin1String string) |
848 | { |
849 | return string.data(); |
850 | } |
851 | |
852 | static inline QByteArray qQmlPropertyCacheToString(QStringView string) |
853 | { |
854 | return string.toUtf8(); |
855 | } |
856 | |
857 | static inline QByteArray qQmlPropertyCacheToString(const QV4::String *string) |
858 | { |
859 | return string->toQString().toUtf8(); |
860 | } |
861 | |
862 | template<typename T> |
863 | const QQmlPropertyData * |
864 | qQmlPropertyCacheProperty(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 | |
892 | const 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 | |
899 | const 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 | |
906 | const 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 |
914 | static 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 | |
922 | const char *QQmlPropertyCache::className() const |
923 | { |
924 | if (const QMetaObject *mo = _metaObject.metaObject()) |
925 | return mo->className(); |
926 | else |
927 | return _dynamicClassName.constData(); |
928 | } |
929 | |
930 | void 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 | |
1062 | namespace { |
1063 | template <typename StringVisitor, typename TypeInfoVisitor> |
1064 | int 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 | |
1106 | template <typename StringVisitor, typename TypeInfoVisitor> |
1107 | int 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 | |
1121 | template <typename StringVisitor> |
1122 | int 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 | |
1137 | template <typename StringVisitor> |
1138 | int 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 | |
1163 | template <typename StringVisitor> |
1164 | int 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 | |
1189 | static_assert(QMetaObjectPrivate::OutputRevision == 12, "Check and adjust determineMetaObjectSizes" ); |
1190 | |
1191 | bool 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 | |
1209 | bool 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 | |
1225 | QByteArray 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 | */ |
1266 | QList<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 | |
1279 | QT_END_NAMESPACE |
1280 | |