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 | #ifndef QQMLPROPERTYCACHE_P_H |
5 | #define QQMLPROPERTYCACHE_P_H |
6 | |
7 | // |
8 | // W A R N I N G |
9 | // ------------- |
10 | // |
11 | // This file is not part of the Qt API. It exists purely as an |
12 | // implementation detail. This header file may change from version to |
13 | // version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | // |
17 | |
18 | #include <private/qlinkedstringhash_p.h> |
19 | #include <private/qqmlenumdata_p.h> |
20 | #include <private/qqmlenumvalue_p.h> |
21 | #include <private/qqmlpropertydata_p.h> |
22 | #include <private/qqmlrefcount_p.h> |
23 | |
24 | #include <QtCore/qvarlengtharray.h> |
25 | #include <QtCore/qvector.h> |
26 | #include <QtCore/qversionnumber.h> |
27 | |
28 | #include <limits> |
29 | |
30 | QT_BEGIN_NAMESPACE |
31 | |
32 | class QCryptographicHash; |
33 | class QJSEngine; |
34 | class QMetaObjectBuilder; |
35 | class QQmlContextData; |
36 | class QQmlPropertyCache; |
37 | class QQmlPropertyCacheMethodArguments; |
38 | class QQmlVMEMetaObject; |
39 | |
40 | class QQmlMetaObjectPointer |
41 | { |
42 | public: |
43 | Q_NODISCARD_CTOR QQmlMetaObjectPointer() = default; |
44 | |
45 | Q_NODISCARD_CTOR QQmlMetaObjectPointer(const QMetaObject *staticMetaObject) |
46 | : d(quintptr(staticMetaObject)) |
47 | { |
48 | Q_ASSERT((d.loadRelaxed() & Shared) == 0); |
49 | } |
50 | |
51 | ~QQmlMetaObjectPointer() |
52 | { |
53 | const auto dd = d.loadAcquire(); |
54 | if (dd & Shared) |
55 | reinterpret_cast<SharedHolder *>(dd ^ Shared)->release(); |
56 | } |
57 | |
58 | private: |
59 | friend class QQmlPropertyCache; |
60 | Q_NODISCARD_CTOR QQmlMetaObjectPointer(const QQmlMetaObjectPointer &other) |
61 | : d(other.d.loadRelaxed()) |
62 | { |
63 | // other has to survive until this ctor is done. So d cannot disappear before. |
64 | const auto od = other.d.loadRelaxed(); |
65 | if (od & Shared) |
66 | reinterpret_cast<SharedHolder *>(od ^ Shared)->addref(); |
67 | } |
68 | |
69 | QQmlMetaObjectPointer(QQmlMetaObjectPointer &&other) = delete; |
70 | QQmlMetaObjectPointer &operator=(QQmlMetaObjectPointer &&other) = delete; |
71 | QQmlMetaObjectPointer &operator=(const QQmlMetaObjectPointer &other) = delete; |
72 | |
73 | public: |
74 | void setSharedOnce(QMetaObject *shared) const |
75 | { |
76 | SharedHolder *holder = new SharedHolder(shared); |
77 | if (!d.testAndSetRelease(expectedValue: 0, newValue: quintptr(holder) | Shared)) |
78 | holder->release(); |
79 | } |
80 | |
81 | const QMetaObject *metaObject() const |
82 | { |
83 | const auto dd = d.loadAcquire(); |
84 | if (dd & Shared) |
85 | return reinterpret_cast<SharedHolder *>(dd ^ Shared)->metaObject; |
86 | return reinterpret_cast<const QMetaObject *>(dd); |
87 | } |
88 | |
89 | bool isShared() const |
90 | { |
91 | // This works because static metaobjects need to be set in the ctor and once a shared |
92 | // metaobject has been set, it cannot be removed anymore. |
93 | const auto dd = d.loadRelaxed(); |
94 | return !dd || (dd & Shared); |
95 | } |
96 | |
97 | bool isNull() const |
98 | { |
99 | return d.loadRelaxed() == 0; |
100 | } |
101 | |
102 | private: |
103 | enum Tag { |
104 | Static = 0, |
105 | Shared = 1 |
106 | }; |
107 | |
108 | struct SharedHolder : public QQmlRefCounted<SharedHolder> |
109 | { |
110 | Q_DISABLE_COPY_MOVE(SharedHolder) |
111 | SharedHolder(QMetaObject *shared) : metaObject(shared) {} |
112 | ~SharedHolder() { free(ptr: metaObject); } |
113 | QMetaObject *metaObject; |
114 | }; |
115 | |
116 | mutable QBasicAtomicInteger<quintptr> d = 0; |
117 | }; |
118 | |
119 | class Q_QML_PRIVATE_EXPORT QQmlPropertyCache |
120 | : public QQmlRefCounted<QQmlPropertyCache> |
121 | { |
122 | public: |
123 | using Ptr = QQmlRefPointer<QQmlPropertyCache>; |
124 | |
125 | struct ConstPtr : public QQmlRefPointer<const QQmlPropertyCache> |
126 | { |
127 | using QQmlRefPointer<const QQmlPropertyCache>::QQmlRefPointer; |
128 | |
129 | ConstPtr(const Ptr &ptr) : ConstPtr(ptr.data(), AddRef) {} |
130 | ConstPtr(Ptr &&ptr) : ConstPtr(ptr.take(), Adopt) {} |
131 | ConstPtr &operator=(const Ptr &ptr) { return operator=(ConstPtr(ptr)); } |
132 | ConstPtr &operator=(Ptr &&ptr) { return operator=(ConstPtr(std::move(ptr))); } |
133 | }; |
134 | |
135 | static Ptr createStandalone( |
136 | const QMetaObject *, QTypeRevision metaObjectRevision = QTypeRevision::zero()); |
137 | |
138 | QQmlPropertyCache() = default; |
139 | ~QQmlPropertyCache() override; |
140 | |
141 | void update(const QMetaObject *); |
142 | void invalidate(const QMetaObject *); |
143 | |
144 | QQmlPropertyCache::Ptr copy() const; |
145 | |
146 | QQmlPropertyCache::Ptr copyAndAppend( |
147 | const QMetaObject *, QTypeRevision typeVersion, |
148 | QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(), |
149 | QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(), |
150 | QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags()) const; |
151 | |
152 | QQmlPropertyCache::Ptr copyAndReserve( |
153 | int propertyCount, int methodCount, int signalCount, int enumCount) const; |
154 | void appendProperty(const QString &, QQmlPropertyData::Flags flags, int coreIndex, |
155 | QMetaType propType, QTypeRevision revision, int notifyIndex); |
156 | void appendSignal(const QString &, QQmlPropertyData::Flags, int coreIndex, |
157 | const QMetaType *types = nullptr, |
158 | const QList<QByteArray> &names = QList<QByteArray>()); |
159 | void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex, |
160 | QMetaType returnType, const QList<QByteArray> &names, |
161 | const QVector<QMetaType> ¶meterTypes); |
162 | void appendEnum(const QString &, const QVector<QQmlEnumValue> &); |
163 | |
164 | const QMetaObject *metaObject() const; |
165 | const QMetaObject *createMetaObject() const; |
166 | const QMetaObject *firstCppMetaObject() const; |
167 | |
168 | template<typename K> |
169 | const QQmlPropertyData *property(const K &key, QObject *object, |
170 | const QQmlRefPointer<QQmlContextData> &context) const |
171 | { |
172 | return findProperty(stringCache.find(key), object, context); |
173 | } |
174 | |
175 | const QQmlPropertyData *property(int) const; |
176 | const QQmlPropertyData *maybeUnresolvedProperty(int) const; |
177 | const QQmlPropertyData *method(int) const; |
178 | const QQmlPropertyData *signal(int index) const; |
179 | QQmlEnumData *qmlEnum(int) const; |
180 | int methodIndexToSignalIndex(int) const; |
181 | |
182 | QString defaultPropertyName() const; |
183 | const QQmlPropertyData *defaultProperty() const; |
184 | |
185 | // Return a reference here so that we don't have to addref/release all the time |
186 | inline const QQmlPropertyCache::ConstPtr &parent() const; |
187 | |
188 | // is used by the Qml Designer |
189 | void setParent(QQmlPropertyCache::ConstPtr newParent); |
190 | |
191 | inline const QQmlPropertyData *overrideData(const QQmlPropertyData *) const; |
192 | inline bool isAllowedInRevision(const QQmlPropertyData *) const; |
193 | |
194 | static const QQmlPropertyData *property( |
195 | QObject *, QStringView, const QQmlRefPointer<QQmlContextData> &, |
196 | QQmlPropertyData *); |
197 | static const QQmlPropertyData *property(QObject *, const QLatin1String &, const QQmlRefPointer<QQmlContextData> &, |
198 | QQmlPropertyData *); |
199 | static const QQmlPropertyData *property(QObject *, const QV4::String *, const QQmlRefPointer<QQmlContextData> &, |
200 | QQmlPropertyData *); |
201 | |
202 | //see QMetaObjectPrivate::originalClone |
203 | int originalClone(int index) const; |
204 | static int originalClone(const QObject *, int index); |
205 | |
206 | QList<QByteArray> signalParameterNames(int index) const; |
207 | static QString signalParameterStringForJS(QV4::ExecutionEngine *engine, const QList<QByteArray> ¶meterNameList, QString *errorString = nullptr); |
208 | |
209 | const char *className() const; |
210 | |
211 | inline int propertyCount() const; |
212 | inline int propertyOffset() const; |
213 | inline int methodCount() const; |
214 | inline int methodOffset() const; |
215 | inline int signalCount() const; |
216 | inline int signalOffset() const; |
217 | inline int qmlEnumCount() const; |
218 | |
219 | void toMetaObjectBuilder(QMetaObjectBuilder &) const; |
220 | |
221 | inline bool callJSFactoryMethod(QObject *object, void **args) const; |
222 | |
223 | static bool determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, int *stringCount); |
224 | static bool addToHash(QCryptographicHash &hash, const QMetaObject &mo); |
225 | |
226 | QByteArray checksum(QHash<quintptr, QByteArray> *checksums, bool *ok) const; |
227 | |
228 | QTypeRevision allowedRevision(int index) const { return allowedRevisionCache[index]; } |
229 | void setAllowedRevision(int index, QTypeRevision allowed) { allowedRevisionCache[index] = allowed; } |
230 | |
231 | private: |
232 | friend class QQmlEnginePrivate; |
233 | friend class QQmlCompiler; |
234 | template <typename T> friend class QQmlPropertyCacheCreator; |
235 | template <typename T> friend class QQmlPropertyCacheAliasCreator; |
236 | template <typename T> friend class QQmlComponentAndAliasResolver; |
237 | friend class QQmlMetaObject; |
238 | |
239 | QQmlPropertyCache(const QQmlMetaObjectPointer &metaObject) : _metaObject(metaObject) {} |
240 | |
241 | inline QQmlPropertyCache::Ptr copy(const QQmlMetaObjectPointer &mo, int reserve) const; |
242 | |
243 | void append(const QMetaObject *, QTypeRevision typeVersion, |
244 | QQmlPropertyData::Flags propertyFlags = QQmlPropertyData::Flags(), |
245 | QQmlPropertyData::Flags methodFlags = QQmlPropertyData::Flags(), |
246 | QQmlPropertyData::Flags signalFlags = QQmlPropertyData::Flags()); |
247 | |
248 | QQmlPropertyCacheMethodArguments *createArgumentsObject(int count, const QList<QByteArray> &names); |
249 | |
250 | typedef QVector<QQmlPropertyData> IndexCache; |
251 | typedef QLinkedStringMultiHash<QPair<int, QQmlPropertyData *> > StringCache; |
252 | typedef QVector<QTypeRevision> AllowedRevisionCache; |
253 | |
254 | const QQmlPropertyData *findProperty(StringCache::ConstIterator it, QObject *, |
255 | const QQmlRefPointer<QQmlContextData> &) const; |
256 | const QQmlPropertyData *findProperty(StringCache::ConstIterator it, const QQmlVMEMetaObject *, |
257 | const QQmlRefPointer<QQmlContextData> &) const; |
258 | |
259 | template<typename K> |
260 | QQmlPropertyData *findNamedProperty(const K &key) const |
261 | { |
262 | StringCache::mapped_type *it = stringCache.value(key); |
263 | return it ? it->second : 0; |
264 | } |
265 | |
266 | template<typename K> |
267 | void setNamedProperty(const K &key, int index, QQmlPropertyData *data) |
268 | { |
269 | stringCache.insert(key, qMakePair(value1&: index, value2&: data)); |
270 | } |
271 | |
272 | private: |
273 | enum OverrideResult { NoOverride, InvalidOverride, ValidOverride }; |
274 | |
275 | template<typename String> |
276 | OverrideResult handleOverride(const String &name, QQmlPropertyData *data, QQmlPropertyData *old) |
277 | { |
278 | if (!old) |
279 | return NoOverride; |
280 | |
281 | if (data->markAsOverrideOf(predecessor: old)) |
282 | return ValidOverride; |
283 | |
284 | qWarning("Final member %s is overridden in class %s. The override won't be used." , |
285 | qPrintable(name), className()); |
286 | return InvalidOverride; |
287 | } |
288 | |
289 | template<typename String> |
290 | OverrideResult handleOverride(const String &name, QQmlPropertyData *data) |
291 | { |
292 | return handleOverride(name, data, findNamedProperty(name)); |
293 | } |
294 | |
295 | int propertyIndexCacheStart = 0; // placed here to avoid gap between QQmlRefCount and _parent |
296 | QQmlPropertyCache::ConstPtr _parent; |
297 | |
298 | IndexCache propertyIndexCache; |
299 | IndexCache methodIndexCache; |
300 | IndexCache signalHandlerIndexCache; |
301 | StringCache stringCache; |
302 | AllowedRevisionCache allowedRevisionCache; |
303 | QVector<QQmlEnumData> enumCache; |
304 | |
305 | QQmlMetaObjectPointer _metaObject; |
306 | QByteArray _dynamicClassName; |
307 | QByteArray _dynamicStringData; |
308 | QByteArray _listPropertyAssignBehavior; |
309 | QString _defaultPropertyName; |
310 | QQmlPropertyCacheMethodArguments *argumentsCache = nullptr; |
311 | int methodIndexCacheStart = 0; |
312 | int signalHandlerIndexCacheStart = 0; |
313 | int _jsFactoryMethodIndex = -1; |
314 | }; |
315 | |
316 | // Returns this property cache's metaObject. May be null if it hasn't been created yet. |
317 | inline const QMetaObject *QQmlPropertyCache::metaObject() const |
318 | { |
319 | return _metaObject.metaObject(); |
320 | } |
321 | |
322 | // Returns the first C++ type's QMetaObject - that is, the first QMetaObject not created by |
323 | // QML |
324 | inline const QMetaObject *QQmlPropertyCache::firstCppMetaObject() const |
325 | { |
326 | const QQmlPropertyCache *p = this; |
327 | while (p->_metaObject.isShared()) |
328 | p = p->parent().data(); |
329 | return p->_metaObject.metaObject(); |
330 | } |
331 | |
332 | inline const QQmlPropertyData *QQmlPropertyCache::property(int index) const |
333 | { |
334 | if (index < 0 || index >= propertyCount()) |
335 | return nullptr; |
336 | |
337 | if (index < propertyIndexCacheStart) |
338 | return _parent->property(index); |
339 | |
340 | return &propertyIndexCache.at(i: index - propertyIndexCacheStart); |
341 | } |
342 | |
343 | inline const QQmlPropertyData *QQmlPropertyCache::method(int index) const |
344 | { |
345 | if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.size())) |
346 | return nullptr; |
347 | |
348 | if (index < methodIndexCacheStart) |
349 | return _parent->method(index); |
350 | |
351 | return const_cast<const QQmlPropertyData *>(&methodIndexCache.at(i: index - methodIndexCacheStart)); |
352 | } |
353 | |
354 | /*! \internal |
355 | \a index MUST be in the signal index range (see QObjectPrivate::signalIndex()). |
356 | This is different from QMetaMethod::methodIndex(). |
357 | */ |
358 | inline const QQmlPropertyData *QQmlPropertyCache::signal(int index) const |
359 | { |
360 | if (index < 0 || index >= (signalHandlerIndexCacheStart + signalHandlerIndexCache.size())) |
361 | return nullptr; |
362 | |
363 | if (index < signalHandlerIndexCacheStart) |
364 | return _parent->signal(index); |
365 | |
366 | const QQmlPropertyData *rv = const_cast<const QQmlPropertyData *>(&methodIndexCache.at(i: index - signalHandlerIndexCacheStart)); |
367 | Q_ASSERT(rv->isSignal() || rv->coreIndex() == -1); |
368 | return rv; |
369 | } |
370 | |
371 | inline QQmlEnumData *QQmlPropertyCache::qmlEnum(int index) const |
372 | { |
373 | if (index < 0 || index >= enumCache.size()) |
374 | return nullptr; |
375 | |
376 | return const_cast<QQmlEnumData *>(&enumCache.at(i: index)); |
377 | } |
378 | |
379 | inline int QQmlPropertyCache::methodIndexToSignalIndex(int index) const |
380 | { |
381 | if (index < 0 || index >= (methodIndexCacheStart + methodIndexCache.size())) |
382 | return index; |
383 | |
384 | if (index < methodIndexCacheStart) |
385 | return _parent->methodIndexToSignalIndex(index); |
386 | |
387 | return index - methodIndexCacheStart + signalHandlerIndexCacheStart; |
388 | } |
389 | |
390 | // Returns the name of the default property for this cache |
391 | inline QString QQmlPropertyCache::defaultPropertyName() const |
392 | { |
393 | return _defaultPropertyName; |
394 | } |
395 | |
396 | inline const QQmlPropertyCache::ConstPtr &QQmlPropertyCache::parent() const |
397 | { |
398 | return _parent; |
399 | } |
400 | |
401 | const QQmlPropertyData * |
402 | QQmlPropertyCache::overrideData(const QQmlPropertyData *data) const |
403 | { |
404 | if (!data->hasOverride()) |
405 | return nullptr; |
406 | |
407 | if (data->overrideIndexIsProperty()) |
408 | return property(index: data->overrideIndex()); |
409 | else |
410 | return method(index: data->overrideIndex()); |
411 | } |
412 | |
413 | bool QQmlPropertyCache::isAllowedInRevision(const QQmlPropertyData *data) const |
414 | { |
415 | const QTypeRevision requested = data->revision(); |
416 | const int offset = data->metaObjectOffset(); |
417 | if (offset == -1 && requested == QTypeRevision::zero()) |
418 | return true; |
419 | |
420 | Q_ASSERT(offset >= 0); |
421 | Q_ASSERT(offset < allowedRevisionCache.size()); |
422 | const QTypeRevision allowed = allowedRevisionCache[offset]; |
423 | |
424 | if (requested.hasMajorVersion()) { |
425 | if (requested.majorVersion() > allowed.majorVersion()) |
426 | return false; |
427 | if (requested.majorVersion() < allowed.majorVersion()) |
428 | return true; |
429 | } |
430 | |
431 | return !requested.hasMinorVersion() || requested.minorVersion() <= allowed.minorVersion(); |
432 | } |
433 | |
434 | int QQmlPropertyCache::propertyCount() const |
435 | { |
436 | return propertyIndexCacheStart + int(propertyIndexCache.size()); |
437 | } |
438 | |
439 | int QQmlPropertyCache::propertyOffset() const |
440 | { |
441 | return propertyIndexCacheStart; |
442 | } |
443 | |
444 | int QQmlPropertyCache::methodCount() const |
445 | { |
446 | return methodIndexCacheStart + int(methodIndexCache.size()); |
447 | } |
448 | |
449 | int QQmlPropertyCache::methodOffset() const |
450 | { |
451 | return methodIndexCacheStart; |
452 | } |
453 | |
454 | int QQmlPropertyCache::signalCount() const |
455 | { |
456 | return signalHandlerIndexCacheStart + int(signalHandlerIndexCache.size()); |
457 | } |
458 | |
459 | int QQmlPropertyCache::signalOffset() const |
460 | { |
461 | return signalHandlerIndexCacheStart; |
462 | } |
463 | |
464 | int QQmlPropertyCache::qmlEnumCount() const |
465 | { |
466 | return int(enumCache.size()); |
467 | } |
468 | |
469 | bool QQmlPropertyCache::callJSFactoryMethod(QObject *object, void **args) const |
470 | { |
471 | if (_jsFactoryMethodIndex != -1) { |
472 | if (const QMetaObject *mo = _metaObject.metaObject()) { |
473 | mo->d.static_metacall(object, QMetaObject::InvokeMetaMethod, |
474 | _jsFactoryMethodIndex, args); |
475 | return true; |
476 | } |
477 | return false; |
478 | } |
479 | if (_parent) |
480 | return _parent->callJSFactoryMethod(object, args); |
481 | return false; |
482 | } |
483 | |
484 | QT_END_NAMESPACE |
485 | |
486 | #endif // QQMLPROPERTYCACHE_P_H |
487 | |