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