1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2019 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 | #ifndef QQMLPROPERTYDATA_P_H |
41 | #define QQMLPROPERTYDATA_P_H |
42 | |
43 | // |
44 | // W A R N I N G |
45 | // ------------- |
46 | // |
47 | // This file is not part of the Qt API. It exists purely as an |
48 | // implementation detail. This header file may change from version to |
49 | // version without notice, or even be removed. |
50 | // |
51 | // We mean it. |
52 | // |
53 | |
54 | #include <private/qobject_p.h> |
55 | #include <QtCore/qglobal.h> |
56 | |
57 | QT_BEGIN_NAMESPACE |
58 | |
59 | class QQmlPropertyCacheMethodArguments; |
60 | class QQmlPropertyData |
61 | { |
62 | public: |
63 | enum WriteFlag { |
64 | BypassInterceptor = 0x01, |
65 | DontRemoveBinding = 0x02, |
66 | RemoveBindingOnAliasWrite = 0x04 |
67 | }; |
68 | Q_DECLARE_FLAGS(WriteFlags, WriteFlag) |
69 | |
70 | typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction; |
71 | |
72 | struct Flags { |
73 | friend class QQmlPropertyData; |
74 | enum Types { |
75 | OtherType = 0, |
76 | FunctionType = 1, // Is an invokable |
77 | QObjectDerivedType = 2, // Property type is a QObject* derived type |
78 | EnumType = 3, // Property type is an enum |
79 | QListType = 4, // Property type is a QML list |
80 | QmlBindingType = 5, // Property type is a QQmlBinding* |
81 | QJSValueType = 6, // Property type is a QScriptValue |
82 | // Gap, used to be V4HandleType |
83 | VarPropertyType = 8, // Property type is a "var" property of VMEMO |
84 | QVariantType = 9 // Property is a QVariant |
85 | }; |
86 | |
87 | // Members of the form aORb can only be a when type is not FunctionType, and only be |
88 | // b when type equals FunctionType. For that reason, the semantic meaning of the bit is |
89 | // overloaded, and the accessor functions are used to get the correct value |
90 | // |
91 | // Moreover, isSignalHandler, isOverload and isCloned and isConstructor make only sense |
92 | // for functions, too (and could at a later point be reused for flags that only make sense |
93 | // for non-functions) |
94 | // |
95 | // Lastly, isDirect and isOverridden apply to both functions and non-functions |
96 | private: |
97 | quint16 isConstantORisVMEFunction : 1; // Has CONST flag OR Function was added by QML |
98 | quint16 isWritableORhasArguments : 1; // Has WRITE function OR Function takes arguments |
99 | quint16 isResettableORisSignal : 1; // Has RESET function OR Function is a signal |
100 | quint16 isAliasORisVMESignal : 1; // Is a QML alias to another property OR Signal was added by QML |
101 | quint16 isFinalORisV4Function : 1; // Has FINAL flag OR Function takes QQmlV4Function* args |
102 | quint16 isSignalHandler : 1; // Function is a signal handler |
103 | quint16 isOverload : 1; // Function is an overload of another function |
104 | quint16 isRequiredORisCloned : 1; // Has REQUIRED flag OR The function was marked as cloned |
105 | quint16 isConstructor : 1; // The function was marked is a constructor |
106 | quint16 isDirect : 1; // Exists on a C++ QMetaObject |
107 | quint16 isOverridden : 1; // Is overridden by a extension property |
108 | public: |
109 | quint16 type : 4; // stores an entry of Types |
110 | |
111 | // Apply only to IsFunctions |
112 | |
113 | // Internal QQmlPropertyCache flags |
114 | quint16 overrideIndexIsProperty: 1; |
115 | |
116 | inline Flags(); |
117 | inline bool operator==(const Flags &other) const; |
118 | inline void copyPropertyTypeFlags(Flags from); |
119 | |
120 | void setIsConstant(bool b) { |
121 | Q_ASSERT(type != FunctionType); |
122 | isConstantORisVMEFunction = b; |
123 | } |
124 | |
125 | void setIsWritable(bool b) { |
126 | Q_ASSERT(type != FunctionType); |
127 | isWritableORhasArguments = b; |
128 | } |
129 | |
130 | void setIsResettable(bool b) { |
131 | Q_ASSERT(type != FunctionType); |
132 | isResettableORisSignal = b; |
133 | } |
134 | |
135 | void setIsAlias(bool b) { |
136 | Q_ASSERT(type != FunctionType); |
137 | isAliasORisVMESignal = b; |
138 | } |
139 | |
140 | void setIsFinal(bool b) { |
141 | Q_ASSERT(type != FunctionType); |
142 | isFinalORisV4Function = b; |
143 | } |
144 | |
145 | void setIsOverridden(bool b) { |
146 | isOverridden = b; |
147 | } |
148 | |
149 | void setIsDirect(bool b) { |
150 | isDirect = b; |
151 | } |
152 | |
153 | void setIsRequired(bool b) { |
154 | Q_ASSERT(type != FunctionType); |
155 | isRequiredORisCloned = b; |
156 | } |
157 | |
158 | void setIsVMEFunction(bool b) { |
159 | Q_ASSERT(type == FunctionType); |
160 | isConstantORisVMEFunction = b; |
161 | } |
162 | void setHasArguments(bool b) { |
163 | Q_ASSERT(type == FunctionType); |
164 | isWritableORhasArguments = b; |
165 | } |
166 | void setIsSignal(bool b) { |
167 | Q_ASSERT(type == FunctionType); |
168 | isResettableORisSignal = b; |
169 | } |
170 | void setIsVMESignal(bool b) { |
171 | Q_ASSERT(type == FunctionType); |
172 | isAliasORisVMESignal = b; |
173 | } |
174 | |
175 | void setIsV4Function(bool b) { |
176 | Q_ASSERT(type == FunctionType); |
177 | isFinalORisV4Function = b; |
178 | } |
179 | |
180 | void setIsSignalHandler(bool b) { |
181 | Q_ASSERT(type == FunctionType); |
182 | isSignalHandler = b; |
183 | } |
184 | |
185 | void setIsOverload(bool b) { |
186 | Q_ASSERT(type == FunctionType); |
187 | isOverload = b; |
188 | } |
189 | |
190 | void setIsCloned(bool b) { |
191 | Q_ASSERT(type == FunctionType); |
192 | isRequiredORisCloned = b; |
193 | } |
194 | |
195 | void setIsConstructor(bool b) { |
196 | Q_ASSERT(type == FunctionType); |
197 | isConstructor = b; |
198 | } |
199 | |
200 | }; |
201 | |
202 | Q_STATIC_ASSERT(sizeof(Flags) == sizeof(quint16)); |
203 | |
204 | inline bool operator==(const QQmlPropertyData &) const; |
205 | |
206 | Flags flags() const { return m_flags; } |
207 | void setFlags(Flags f) { m_flags = f; } |
208 | |
209 | bool isValid() const { return coreIndex() != -1; } |
210 | |
211 | bool isConstant() const { return !isFunction() && m_flags.isConstantORisVMEFunction; } |
212 | bool isWritable() const { return !isFunction() && m_flags.isWritableORhasArguments; } |
213 | void setWritable(bool onoff) { Q_ASSERT(!isFunction()); m_flags.isWritableORhasArguments = onoff; } |
214 | bool isResettable() const { return !isFunction() && m_flags.isResettableORisSignal; } |
215 | bool isAlias() const { return !isFunction() && m_flags.isAliasORisVMESignal; } |
216 | bool isFinal() const { return !isFunction() && m_flags.isFinalORisV4Function; } |
217 | bool isOverridden() const { return m_flags.isOverridden; } |
218 | bool isDirect() const { return m_flags.isDirect; } |
219 | bool isRequired() const { return !isFunction() && m_flags.isRequiredORisCloned; } |
220 | bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; } |
221 | bool isFunction() const { return m_flags.type == Flags::FunctionType; } |
222 | bool isQObject() const { return m_flags.type == Flags::QObjectDerivedType; } |
223 | bool isEnum() const { return m_flags.type == Flags::EnumType; } |
224 | bool isQList() const { return m_flags.type == Flags::QListType; } |
225 | bool isQmlBinding() const { return m_flags.type == Flags::QmlBindingType; } |
226 | bool isQJSValue() const { return m_flags.type == Flags::QJSValueType; } |
227 | bool isVarProperty() const { return m_flags.type == Flags::VarPropertyType; } |
228 | bool isQVariant() const { return m_flags.type == Flags::QVariantType; } |
229 | bool isVMEFunction() const { return isFunction() && m_flags.isConstantORisVMEFunction; } |
230 | bool hasArguments() const { return isFunction() && m_flags.isWritableORhasArguments; } |
231 | bool isSignal() const { return isFunction() && m_flags.isResettableORisSignal; } |
232 | bool isVMESignal() const { return isFunction() && m_flags.isAliasORisVMESignal; } |
233 | bool isV4Function() const { return isFunction() && m_flags.isFinalORisV4Function; } |
234 | bool isSignalHandler() const { return m_flags.isSignalHandler; } |
235 | bool isOverload() const { return m_flags.isOverload; } |
236 | void setOverload(bool onoff) { m_flags.isOverload = onoff; } |
237 | bool isCloned() const { return isFunction() && m_flags.isRequiredORisCloned; } |
238 | bool isConstructor() const { return m_flags.isConstructor; } |
239 | |
240 | bool hasOverride() const { return overrideIndex() >= 0; } |
241 | bool hasRevision() const { return revision() != 0; } |
242 | |
243 | // This is unsafe in the general case. The property might be in the process of getting |
244 | // resolved. Only use it if this case has been taken into account. |
245 | bool isResolved() const { return m_propTypeAndRelativePropIndex != 0; } |
246 | |
247 | int propType() const |
248 | { |
249 | const quint32 type = m_propTypeAndRelativePropIndex & PropTypeMask; |
250 | Q_ASSERT(type > 0); // Property has to be fully resolved. |
251 | return type == PropTypeUnknown ? 0 : type; |
252 | } |
253 | |
254 | void setPropType(int pt) |
255 | { |
256 | // You can only directly set the property type if you own the QQmlPropertyData. |
257 | // It must not be exposed to other threads before setting the type! |
258 | Q_ASSERT(pt >= 0); |
259 | Q_ASSERT(uint(pt) < PropTypeUnknown); |
260 | m_propTypeAndRelativePropIndex |
261 | = (m_propTypeAndRelativePropIndex & RelativePropIndexMask) |
262 | | (pt == 0 ? PropTypeUnknown : quint32(pt)); |
263 | } |
264 | |
265 | int notifyIndex() const { return m_notifyIndex; } |
266 | void setNotifyIndex(int idx) |
267 | { |
268 | Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); |
269 | Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); |
270 | m_notifyIndex = qint16(idx); |
271 | } |
272 | |
273 | bool overrideIndexIsProperty() const { return m_flags.overrideIndexIsProperty; } |
274 | void setOverrideIndexIsProperty(bool onoff) { m_flags.overrideIndexIsProperty = onoff; } |
275 | |
276 | int overrideIndex() const { return m_overrideIndex; } |
277 | void setOverrideIndex(int idx) |
278 | { |
279 | Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); |
280 | Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); |
281 | m_overrideIndex = qint16(idx); |
282 | } |
283 | |
284 | int coreIndex() const { return m_coreIndex; } |
285 | void setCoreIndex(int idx) |
286 | { |
287 | Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); |
288 | Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); |
289 | m_coreIndex = qint16(idx); |
290 | } |
291 | |
292 | quint8 revision() const { return m_revision; } |
293 | void setRevision(quint8 rev) |
294 | { |
295 | Q_ASSERT(rev <= std::numeric_limits<quint8>::max()); |
296 | m_revision = quint8(rev); |
297 | } |
298 | |
299 | /* If a property is a C++ type, then we store the minor |
300 | * version of this type. |
301 | * This is required to resolve property or signal revisions |
302 | * if this property is used as a grouped property. |
303 | * |
304 | * Test.qml |
305 | * property TextEdit someTextEdit: TextEdit {} |
306 | * |
307 | * Test { |
308 | * someTextEdit.preeditText: "test" //revision 7 |
309 | * someTextEdit.onEditingFinished: console.log("test") //revision 6 |
310 | * } |
311 | * |
312 | * To determine if these properties with revisions are available we need |
313 | * the minor version of TextEdit as imported in Test.qml. |
314 | * |
315 | */ |
316 | |
317 | quint8 typeMinorVersion() const { return m_typeMinorVersion; } |
318 | void setTypeMinorVersion(quint8 rev) |
319 | { |
320 | Q_ASSERT(rev <= std::numeric_limits<quint8>::max()); |
321 | m_typeMinorVersion = quint8(rev); |
322 | } |
323 | |
324 | QQmlPropertyCacheMethodArguments *arguments() const { return m_arguments; } |
325 | bool setArguments(QQmlPropertyCacheMethodArguments *args) |
326 | { |
327 | return m_arguments.testAndSetRelease(expectedValue: nullptr, newValue: args); |
328 | } |
329 | |
330 | int metaObjectOffset() const { return m_metaObjectOffset; } |
331 | void setMetaObjectOffset(int off) |
332 | { |
333 | Q_ASSERT(off >= std::numeric_limits<qint16>::min()); |
334 | Q_ASSERT(off <= std::numeric_limits<qint16>::max()); |
335 | m_metaObjectOffset = qint16(off); |
336 | } |
337 | |
338 | StaticMetaCallFunction staticMetaCallFunction() const { return m_staticMetaCallFunction; } |
339 | void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex) |
340 | { |
341 | if (relativePropertyIndex > std::numeric_limits<quint16>::max()) |
342 | return; |
343 | |
344 | const quint16 propType = m_propTypeAndRelativePropIndex & PropTypeMask; |
345 | if (propType > 0) { |
346 | // We can do this because we know that resolve() has run at this point |
347 | // and we don't need to synchronize anymore. If we get a 0, that means it hasn't |
348 | // run or is currently in progress. We don't want to interfer and just go through |
349 | // the meta object. |
350 | m_propTypeAndRelativePropIndex |
351 | = propType | (relativePropertyIndex << RelativePropIndexShift); |
352 | m_staticMetaCallFunction = f; |
353 | } |
354 | } |
355 | |
356 | quint16 relativePropertyIndex() const |
357 | { |
358 | Q_ASSERT(hasStaticMetaCallFunction()); |
359 | return m_propTypeAndRelativePropIndex >> 16; |
360 | } |
361 | |
362 | static Flags flagsForProperty(const QMetaProperty &); |
363 | void load(const QMetaProperty &); |
364 | void load(const QMetaMethod &); |
365 | QString name(QObject *) const; |
366 | QString name(const QMetaObject *) const; |
367 | |
368 | void markAsOverrideOf(QQmlPropertyData *predecessor); |
369 | |
370 | inline void readProperty(QObject *target, void *property) const |
371 | { |
372 | void *args[] = { property, nullptr }; |
373 | readPropertyWithArgs(target, args); |
374 | } |
375 | |
376 | inline void readPropertyWithArgs(QObject *target, void *args[]) const |
377 | { |
378 | if (hasStaticMetaCallFunction()) |
379 | staticMetaCallFunction()(target, QMetaObject::ReadProperty, relativePropertyIndex(), args); |
380 | else if (isDirect()) |
381 | target->qt_metacall(QMetaObject::ReadProperty, coreIndex(), args); |
382 | else |
383 | QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex(), args); |
384 | } |
385 | |
386 | bool writeProperty(QObject *target, void *value, WriteFlags flags) const |
387 | { |
388 | int status = -1; |
389 | void *argv[] = { value, nullptr, &status, &flags }; |
390 | if (flags.testFlag(flag: BypassInterceptor) && hasStaticMetaCallFunction()) |
391 | staticMetaCallFunction()(target, QMetaObject::WriteProperty, relativePropertyIndex(), argv); |
392 | else if (flags.testFlag(flag: BypassInterceptor) && isDirect()) |
393 | target->qt_metacall(QMetaObject::WriteProperty, coreIndex(), argv); |
394 | else |
395 | QMetaObject::metacall(target, QMetaObject::WriteProperty, coreIndex(), argv); |
396 | return true; |
397 | } |
398 | |
399 | static Flags defaultSignalFlags() |
400 | { |
401 | Flags f; |
402 | f.type = Flags::FunctionType; |
403 | f.setIsSignal(true); |
404 | f.setIsVMESignal(true); |
405 | return f; |
406 | } |
407 | |
408 | static Flags defaultSlotFlags() |
409 | { |
410 | Flags f; |
411 | f.type = Flags::FunctionType; |
412 | f.setIsVMEFunction(true); |
413 | return f; |
414 | } |
415 | |
416 | private: |
417 | friend class QQmlPropertyCache; |
418 | void lazyLoad(const QMetaProperty &); |
419 | void lazyLoad(const QMetaMethod &); |
420 | |
421 | enum { |
422 | PropTypeMask = 0x0000ffff, |
423 | RelativePropIndexMask = 0xffff0000, |
424 | RelativePropIndexShift = 16, |
425 | PropTypeUnknown = std::numeric_limits<quint16>::max(), |
426 | }; |
427 | QAtomicInteger<quint32> m_propTypeAndRelativePropIndex; |
428 | |
429 | Flags m_flags; |
430 | qint16 m_coreIndex = -1; |
431 | |
432 | // The notify index is in the range returned by QObjectPrivate::signalIndex(). |
433 | // This is different from QMetaMethod::methodIndex(). |
434 | qint16 m_notifyIndex = -1; |
435 | qint16 m_overrideIndex = -1; |
436 | |
437 | quint8 m_revision = 0; |
438 | quint8 m_typeMinorVersion = 0; |
439 | qint16 m_metaObjectOffset = -1; |
440 | |
441 | QAtomicPointer<QQmlPropertyCacheMethodArguments> m_arguments; |
442 | StaticMetaCallFunction m_staticMetaCallFunction = nullptr; |
443 | }; |
444 | |
445 | #if QT_POINTER_SIZE == 4 |
446 | Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 24); |
447 | #else // QT_POINTER_SIZE == 8 |
448 | Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 32); |
449 | #endif |
450 | |
451 | bool QQmlPropertyData::operator==(const QQmlPropertyData &other) const |
452 | { |
453 | return flags() == other.flags() && |
454 | propType() == other.propType() && |
455 | coreIndex() == other.coreIndex() && |
456 | notifyIndex() == other.notifyIndex() && |
457 | revision() == other.revision(); |
458 | } |
459 | |
460 | QQmlPropertyData::Flags::Flags() |
461 | : isConstantORisVMEFunction(false) |
462 | , isWritableORhasArguments(false) |
463 | , isResettableORisSignal(false) |
464 | , isAliasORisVMESignal(false) |
465 | , isFinalORisV4Function(false) |
466 | , isSignalHandler(false) |
467 | , isOverload(false) |
468 | , isRequiredORisCloned(false) |
469 | , isConstructor(false) |
470 | , isDirect(false) |
471 | , isOverridden(false) |
472 | , type(OtherType) |
473 | , overrideIndexIsProperty(false) |
474 | {} |
475 | |
476 | bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) const |
477 | { |
478 | return isConstantORisVMEFunction == other.isConstantORisVMEFunction && |
479 | isWritableORhasArguments == other.isWritableORhasArguments && |
480 | isResettableORisSignal == other.isResettableORisSignal && |
481 | isAliasORisVMESignal == other.isAliasORisVMESignal && |
482 | isFinalORisV4Function == other.isFinalORisV4Function && |
483 | isOverridden == other.isOverridden && |
484 | isSignalHandler == other.isSignalHandler && |
485 | isRequiredORisCloned == other.isRequiredORisCloned && |
486 | type == other.type && |
487 | isConstructor == other.isConstructor && |
488 | overrideIndexIsProperty == other.overrideIndexIsProperty; |
489 | } |
490 | |
491 | void QQmlPropertyData::Flags::copyPropertyTypeFlags(QQmlPropertyData::Flags from) |
492 | { |
493 | switch (from.type) { |
494 | case QObjectDerivedType: |
495 | case EnumType: |
496 | case QListType: |
497 | case QmlBindingType: |
498 | case QJSValueType: |
499 | case QVariantType: |
500 | type = from.type; |
501 | } |
502 | } |
503 | |
504 | Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyData::WriteFlags) |
505 | |
506 | QT_END_NAMESPACE |
507 | |
508 | #endif // QQMLPROPERTYDATA_P_H |
509 | |