1 | // Copyright (C) 2019 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 QQMLPROPERTYDATA_P_H |
5 | #define QQMLPROPERTYDATA_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/qobject_p.h> |
19 | #include <QtCore/qglobal.h> |
20 | #include <QtCore/qversionnumber.h> |
21 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | class QQmlPropertyCacheMethodArguments; |
25 | class QQmlPropertyData |
26 | { |
27 | public: |
28 | enum WriteFlag { |
29 | BypassInterceptor = 0x01, |
30 | DontRemoveBinding = 0x02, |
31 | RemoveBindingOnAliasWrite = 0x04, |
32 | HasInternalIndex = 0x8, |
33 | }; |
34 | Q_DECLARE_FLAGS(WriteFlags, WriteFlag) |
35 | |
36 | typedef QObjectPrivate::StaticMetaCallFunction StaticMetaCallFunction; |
37 | |
38 | struct Flags { |
39 | friend class QQmlPropertyData; |
40 | enum Types { |
41 | OtherType = 0, |
42 | FunctionType = 1, // Is an invokable |
43 | QObjectDerivedType = 2, // Property type is a QObject* derived type |
44 | EnumType = 3, // Property type is an enum |
45 | QListType = 4, // Property type is a QML list |
46 | /*QmlBindingType = 5; was: Property type is a QQmlBinding*; now unused */ |
47 | QJSValueType = 6, // Property type is a QScriptValue |
48 | // Gap, used to be V4HandleType |
49 | VarPropertyType = 8, // Property type is a "var" property of VMEMO |
50 | QVariantType = 9, // Property is a QVariant |
51 | ValueType = 10 // Property type is a custom value type |
52 | }; |
53 | |
54 | // The _otherBits (which "pad" the Flags struct to align it nicely) are used |
55 | // to store the relative property index. It will only get used when said index fits. See |
56 | // trySetStaticMetaCallFunction for details. |
57 | // (Note: this padding is done here, because certain compilers have surprising behavior |
58 | // when an enum is declared in-between two bit fields.) |
59 | enum { BitsLeftInFlags = 16 }; |
60 | unsigned otherBits : BitsLeftInFlags; // align to 32 bits |
61 | |
62 | // Members of the form aORb can only be a when type is not FunctionType, and only be |
63 | // b when type equals FunctionType. For that reason, the semantic meaning of the bit is |
64 | // overloaded, and the accessor functions are used to get the correct value |
65 | // |
66 | // Moreover, isSignalHandler, isOverload and isCloned make only sense |
67 | // for functions, too (and could at a later point be reused for flags that only make sense |
68 | // for non-functions) |
69 | // |
70 | // Lastly, isDirect and isOverridden apply to both functions and non-functions |
71 | private: |
72 | unsigned isConst : 1; // Property: has CONST flag/Method: is const |
73 | unsigned isVMEFunction : 1; // Function was added by QML |
74 | unsigned isWritableORhasArguments : 1; // Has WRITE function OR Function takes arguments |
75 | unsigned isResettableORisSignal : 1; // Has RESET function OR Function is a signal |
76 | unsigned isAliasORisVMESignal : 1; // Is a QML alias to another property OR Signal was added by QML |
77 | unsigned isFinalORisV4Function : 1; // Has FINAL flag OR Function takes QQmlV4Function* args |
78 | unsigned isSignalHandler : 1; // Function is a signal handler |
79 | unsigned isOverload : 1; // Function is an overload of another function |
80 | unsigned isRequiredORisCloned : 1; // Has REQUIRED flag OR The function was marked as cloned |
81 | unsigned isConstructorORisBindable : 1; // The function was marked is a constructor OR property is backed by QProperty<T> |
82 | unsigned isOverridden : 1; // Is overridden by a extension property |
83 | public: |
84 | unsigned type : 4; // stores an entry of Types |
85 | |
86 | // Apply only to IsFunctions |
87 | |
88 | // Internal QQmlPropertyCache flags |
89 | unsigned overrideIndexIsProperty: 1; |
90 | |
91 | inline Flags(); |
92 | inline bool operator==(const Flags &other) const; |
93 | inline void copyPropertyTypeFlags(Flags from); |
94 | |
95 | void setIsConstant(bool b) { |
96 | isConst = b; |
97 | } |
98 | |
99 | void setIsWritable(bool b) { |
100 | Q_ASSERT(type != FunctionType); |
101 | isWritableORhasArguments = b; |
102 | } |
103 | |
104 | void setIsResettable(bool b) { |
105 | Q_ASSERT(type != FunctionType); |
106 | isResettableORisSignal = b; |
107 | } |
108 | |
109 | void setIsAlias(bool b) { |
110 | Q_ASSERT(type != FunctionType); |
111 | isAliasORisVMESignal = b; |
112 | } |
113 | |
114 | void setIsFinal(bool b) { |
115 | Q_ASSERT(type != FunctionType); |
116 | isFinalORisV4Function = b; |
117 | } |
118 | |
119 | void setIsOverridden(bool b) { |
120 | isOverridden = b; |
121 | } |
122 | |
123 | void setIsBindable(bool b) { |
124 | Q_ASSERT(type != FunctionType); |
125 | isConstructorORisBindable = b; |
126 | } |
127 | |
128 | void setIsRequired(bool b) { |
129 | Q_ASSERT(type != FunctionType); |
130 | isRequiredORisCloned = b; |
131 | } |
132 | |
133 | void setIsVMEFunction(bool b) { |
134 | Q_ASSERT(type == FunctionType); |
135 | isVMEFunction = b; |
136 | } |
137 | void setHasArguments(bool b) { |
138 | Q_ASSERT(type == FunctionType); |
139 | isWritableORhasArguments = b; |
140 | } |
141 | void setIsSignal(bool b) { |
142 | Q_ASSERT(type == FunctionType); |
143 | isResettableORisSignal = b; |
144 | } |
145 | void setIsVMESignal(bool b) { |
146 | Q_ASSERT(type == FunctionType); |
147 | isAliasORisVMESignal = b; |
148 | } |
149 | |
150 | void setIsV4Function(bool b) { |
151 | Q_ASSERT(type == FunctionType); |
152 | isFinalORisV4Function = b; |
153 | } |
154 | |
155 | void setIsSignalHandler(bool b) { |
156 | Q_ASSERT(type == FunctionType); |
157 | isSignalHandler = b; |
158 | } |
159 | |
160 | void setIsOverload(bool b) { |
161 | Q_ASSERT(type == FunctionType); |
162 | isOverload = b; |
163 | } |
164 | |
165 | void setIsCloned(bool b) { |
166 | Q_ASSERT(type == FunctionType); |
167 | isRequiredORisCloned = b; |
168 | } |
169 | |
170 | void setIsConstructor(bool b) { |
171 | Q_ASSERT(type == FunctionType); |
172 | isConstructorORisBindable = b; |
173 | } |
174 | |
175 | }; |
176 | |
177 | |
178 | inline bool operator==(const QQmlPropertyData &) const; |
179 | |
180 | Flags flags() const { return m_flags; } |
181 | void setFlags(Flags f) |
182 | { |
183 | unsigned otherBits = m_flags.otherBits; |
184 | m_flags = f; |
185 | m_flags.otherBits = otherBits; |
186 | } |
187 | |
188 | bool isValid() const { return coreIndex() != -1; } |
189 | |
190 | bool isConstant() const { return m_flags.isConst; } |
191 | bool isWritable() const { return !isFunction() && m_flags.isWritableORhasArguments; } |
192 | void setWritable(bool onoff) { Q_ASSERT(!isFunction()); m_flags.isWritableORhasArguments = onoff; } |
193 | bool isResettable() const { return !isFunction() && m_flags.isResettableORisSignal; } |
194 | bool isAlias() const { return !isFunction() && m_flags.isAliasORisVMESignal; } |
195 | bool isFinal() const { return !isFunction() && m_flags.isFinalORisV4Function; } |
196 | bool isOverridden() const { return m_flags.isOverridden; } |
197 | bool isRequired() const { return !isFunction() && m_flags.isRequiredORisCloned; } |
198 | bool hasStaticMetaCallFunction() const { return staticMetaCallFunction() != nullptr; } |
199 | bool isFunction() const { return m_flags.type == Flags::FunctionType; } |
200 | bool isQObject() const { return m_flags.type == Flags::QObjectDerivedType; } |
201 | bool isEnum() const { return m_flags.type == Flags::EnumType; } |
202 | bool isQList() const { return m_flags.type == Flags::QListType; } |
203 | bool isQJSValue() const { return m_flags.type == Flags::QJSValueType; } |
204 | bool isVarProperty() const { return m_flags.type == Flags::VarPropertyType; } |
205 | bool isQVariant() const { return m_flags.type == Flags::QVariantType; } |
206 | bool isVMEFunction() const { return isFunction() && m_flags.isVMEFunction; } |
207 | bool hasArguments() const { return isFunction() && m_flags.isWritableORhasArguments; } |
208 | bool isSignal() const { return isFunction() && m_flags.isResettableORisSignal; } |
209 | bool isVMESignal() const { return isFunction() && m_flags.isAliasORisVMESignal; } |
210 | bool isV4Function() const { return isFunction() && m_flags.isFinalORisV4Function; } |
211 | bool isSignalHandler() const { return m_flags.isSignalHandler; } |
212 | bool isOverload() const { return m_flags.isOverload; } |
213 | void setOverload(bool onoff) { m_flags.isOverload = onoff; } |
214 | bool isCloned() const { return isFunction() && m_flags.isRequiredORisCloned; } |
215 | bool isConstructor() const { return isFunction() && m_flags.isConstructorORisBindable; } |
216 | bool isBindable() const { return !isFunction() && m_flags.isConstructorORisBindable; } |
217 | |
218 | bool hasOverride() const { return overrideIndex() >= 0; } |
219 | bool hasRevision() const { return revision() != QTypeRevision::zero(); } |
220 | |
221 | QMetaType propType() const { return m_propType; } |
222 | void setPropType(QMetaType pt) |
223 | { |
224 | m_propType = pt; |
225 | } |
226 | |
227 | int notifyIndex() const { return m_notifyIndex; } |
228 | void setNotifyIndex(int idx) |
229 | { |
230 | Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); |
231 | Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); |
232 | m_notifyIndex = qint16(idx); |
233 | } |
234 | |
235 | bool overrideIndexIsProperty() const { return m_flags.overrideIndexIsProperty; } |
236 | void setOverrideIndexIsProperty(bool onoff) { m_flags.overrideIndexIsProperty = onoff; } |
237 | |
238 | int overrideIndex() const { return m_overrideIndex; } |
239 | void setOverrideIndex(int idx) |
240 | { |
241 | Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); |
242 | Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); |
243 | m_overrideIndex = qint16(idx); |
244 | } |
245 | |
246 | int coreIndex() const { return m_coreIndex; } |
247 | void setCoreIndex(int idx) |
248 | { |
249 | Q_ASSERT(idx >= std::numeric_limits<qint16>::min()); |
250 | Q_ASSERT(idx <= std::numeric_limits<qint16>::max()); |
251 | m_coreIndex = qint16(idx); |
252 | } |
253 | |
254 | QTypeRevision revision() const { return m_revision; } |
255 | void setRevision(QTypeRevision revision) { m_revision = revision; } |
256 | |
257 | /* If a property is a C++ type, then we store the minor |
258 | * version of this type. |
259 | * This is required to resolve property or signal revisions |
260 | * if this property is used as a grouped property. |
261 | * |
262 | * Test.qml |
263 | * property TextEdit someTextEdit: TextEdit {} |
264 | * |
265 | * Test { |
266 | * someTextEdit.preeditText: "test" //revision 7 |
267 | * someTextEdit.onEditingFinished: console.log("test") //revision 6 |
268 | * } |
269 | * |
270 | * To determine if these properties with revisions are available we need |
271 | * the minor version of TextEdit as imported in Test.qml. |
272 | * |
273 | */ |
274 | |
275 | QTypeRevision typeVersion() const { return m_typeVersion; } |
276 | void setTypeVersion(QTypeRevision typeVersion) { m_typeVersion = typeVersion; } |
277 | |
278 | QQmlPropertyCacheMethodArguments *arguments() const { return m_arguments; } |
279 | void setArguments(QQmlPropertyCacheMethodArguments *args) { m_arguments = args; } |
280 | |
281 | int metaObjectOffset() const { return m_metaObjectOffset; } |
282 | void setMetaObjectOffset(int off) |
283 | { |
284 | Q_ASSERT(off >= std::numeric_limits<qint16>::min()); |
285 | Q_ASSERT(off <= std::numeric_limits<qint16>::max()); |
286 | m_metaObjectOffset = qint16(off); |
287 | } |
288 | |
289 | StaticMetaCallFunction staticMetaCallFunction() const { Q_ASSERT(!isFunction()); return m_staticMetaCallFunction; } |
290 | void trySetStaticMetaCallFunction(StaticMetaCallFunction f, unsigned relativePropertyIndex) |
291 | { |
292 | Q_ASSERT(!isFunction()); |
293 | if (relativePropertyIndex < (1 << Flags::BitsLeftInFlags) - 1) { |
294 | m_flags.otherBits = relativePropertyIndex; |
295 | m_staticMetaCallFunction = f; |
296 | } |
297 | } |
298 | quint16 relativePropertyIndex() const { Q_ASSERT(hasStaticMetaCallFunction()); return m_flags.otherBits; } |
299 | |
300 | static Flags flagsForProperty(const QMetaProperty &); |
301 | void load(const QMetaProperty &); |
302 | void load(const QMetaMethod &); |
303 | QString name(QObject *) const; |
304 | QString name(const QMetaObject *) const; |
305 | |
306 | bool markAsOverrideOf(QQmlPropertyData *predecessor); |
307 | |
308 | inline void readProperty(QObject *target, void *property) const |
309 | { |
310 | void *args[] = { property, nullptr }; |
311 | readPropertyWithArgs(target, args); |
312 | } |
313 | |
314 | // This is the same as QMetaObject::metacall(), but inlined here to avoid a function call. |
315 | // And we ignore the return value. |
316 | template<QMetaObject::Call call> |
317 | void doMetacall(QObject *object, int idx, void **argv) const |
318 | { |
319 | if (QDynamicMetaObjectData *dynamicMetaObject = QObjectPrivate::get(o: object)->metaObject) |
320 | dynamicMetaObject->metaCall(object, call, id: idx, argv); |
321 | else |
322 | object->qt_metacall(call, idx, argv); |
323 | } |
324 | |
325 | void readPropertyWithArgs(QObject *target, void *args[]) const |
326 | { |
327 | if (hasStaticMetaCallFunction()) |
328 | staticMetaCallFunction()(target, QMetaObject::ReadProperty, relativePropertyIndex(), args); |
329 | else |
330 | doMetacall<QMetaObject::ReadProperty>(object: target, idx: coreIndex(), argv: args); |
331 | } |
332 | |
333 | bool writeProperty(QObject *target, void *value, WriteFlags flags) const |
334 | { |
335 | int status = -1; |
336 | void *argv[] = { value, nullptr, &status, &flags }; |
337 | if (flags.testFlag(flag: BypassInterceptor) && hasStaticMetaCallFunction()) |
338 | staticMetaCallFunction()(target, QMetaObject::WriteProperty, relativePropertyIndex(), argv); |
339 | else |
340 | doMetacall<QMetaObject::WriteProperty>(object: target, idx: coreIndex(), argv); |
341 | return true; |
342 | } |
343 | |
344 | bool resetProperty(QObject *target, WriteFlags flags) const |
345 | { |
346 | if (flags.testFlag(flag: BypassInterceptor) && hasStaticMetaCallFunction()) |
347 | staticMetaCallFunction()(target, QMetaObject::ResetProperty, relativePropertyIndex(), nullptr); |
348 | else |
349 | doMetacall<QMetaObject::ResetProperty>(object: target, idx: coreIndex(), argv: nullptr); |
350 | return true; |
351 | } |
352 | |
353 | static Flags defaultSignalFlags() |
354 | { |
355 | Flags f; |
356 | f.type = Flags::FunctionType; |
357 | f.setIsSignal(true); |
358 | f.setIsVMESignal(true); |
359 | return f; |
360 | } |
361 | |
362 | static Flags defaultSlotFlags() |
363 | { |
364 | Flags f; |
365 | f.type = Flags::FunctionType; |
366 | f.setIsVMEFunction(true); |
367 | return f; |
368 | } |
369 | |
370 | private: |
371 | friend class QQmlPropertyCache; |
372 | |
373 | Flags m_flags; |
374 | qint16 m_coreIndex = -1; |
375 | |
376 | // The notify index is in the range returned by QObjectPrivate::signalIndex(). |
377 | // This is different from QMetaMethod::methodIndex(). |
378 | qint16 m_notifyIndex = -1; |
379 | qint16 m_overrideIndex = -1; |
380 | |
381 | qint16 m_metaObjectOffset = -1; |
382 | |
383 | QTypeRevision m_revision = QTypeRevision::zero(); |
384 | QTypeRevision m_typeVersion = QTypeRevision::zero(); |
385 | |
386 | QMetaType m_propType = {}; |
387 | |
388 | union { |
389 | QQmlPropertyCacheMethodArguments *m_arguments = nullptr; |
390 | StaticMetaCallFunction m_staticMetaCallFunction; |
391 | }; |
392 | }; |
393 | |
394 | #if QT_POINTER_SIZE == 4 |
395 | Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 24); |
396 | #else // QT_POINTER_SIZE == 8 |
397 | Q_STATIC_ASSERT(sizeof(QQmlPropertyData) == 32); |
398 | #endif |
399 | |
400 | static_assert(std::is_trivially_copyable<QQmlPropertyData>::value); |
401 | |
402 | bool QQmlPropertyData::operator==(const QQmlPropertyData &other) const |
403 | { |
404 | return flags() == other.flags() && |
405 | propType() == other.propType() && |
406 | coreIndex() == other.coreIndex() && |
407 | notifyIndex() == other.notifyIndex() && |
408 | revision() == other.revision(); |
409 | } |
410 | |
411 | QQmlPropertyData::Flags::Flags() |
412 | : otherBits(0) |
413 | , isConst(false) |
414 | , isVMEFunction(false) |
415 | , isWritableORhasArguments(false) |
416 | , isResettableORisSignal(false) |
417 | , isAliasORisVMESignal(false) |
418 | , isFinalORisV4Function(false) |
419 | , isSignalHandler(false) |
420 | , isOverload(false) |
421 | , isRequiredORisCloned(false) |
422 | , isConstructorORisBindable(false) |
423 | , isOverridden(false) |
424 | , type(OtherType) |
425 | , overrideIndexIsProperty(false) |
426 | {} |
427 | |
428 | bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) const |
429 | { |
430 | return isConst == other.isConst && |
431 | isVMEFunction == other.isVMEFunction && |
432 | isWritableORhasArguments == other.isWritableORhasArguments && |
433 | isResettableORisSignal == other.isResettableORisSignal && |
434 | isAliasORisVMESignal == other.isAliasORisVMESignal && |
435 | isFinalORisV4Function == other.isFinalORisV4Function && |
436 | isOverridden == other.isOverridden && |
437 | isSignalHandler == other.isSignalHandler && |
438 | isRequiredORisCloned == other.isRequiredORisCloned && |
439 | type == other.type && |
440 | isConstructorORisBindable == other.isConstructorORisBindable && |
441 | overrideIndexIsProperty == other.overrideIndexIsProperty; |
442 | } |
443 | |
444 | void QQmlPropertyData::Flags::copyPropertyTypeFlags(QQmlPropertyData::Flags from) |
445 | { |
446 | switch (from.type) { |
447 | case QObjectDerivedType: |
448 | case EnumType: |
449 | case QListType: |
450 | case QJSValueType: |
451 | case QVariantType: |
452 | type = from.type; |
453 | } |
454 | } |
455 | |
456 | Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlPropertyData::WriteFlags) |
457 | |
458 | QT_END_NAMESPACE |
459 | |
460 | #endif // QQMLPROPERTYDATA_P_H |
461 | |