Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlpropertybinding_p.h
Go to the documentation of this file.
1// Copyright (C) 2020 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// Qt-Security score:significant
4
5#ifndef QQMLPROPERTYBINDING_P_H
6#define QQMLPROPERTYBINDING_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include <private/qqmljavascriptexpression_p.h>
20#include <private/qqmlpropertydata_p.h>
21#include <private/qv4alloca_p.h>
22#include <private/qqmltranslation_p.h>
23
24#include <QtCore/qproperty.h>
25
26#include <memory>
27
28QT_BEGIN_NAMESPACE
29
30namespace QV4 {
31 struct BoundFunction;
32}
33
34class QQmlPropertyBinding;
35class QQmlScriptString;
36
37class Q_QML_EXPORT QQmlPropertyBindingJS : public QQmlJavaScriptExpression
38{
39 bool mustCaptureBindableProperty() const final {return false;}
40
41 friend class QQmlPropertyBinding;
42 void expressionChanged() override;
43 QQmlPropertyBinding *asBinding()
44 {
45 return const_cast<QQmlPropertyBinding *>(static_cast<const QQmlPropertyBindingJS *>(this)->asBinding());
46 }
47
48 inline QQmlPropertyBinding const *asBinding() const;
49};
50
51class Q_QML_EXPORT QQmlPropertyBindingJSForBoundFunction : public QQmlPropertyBindingJS
52{
53public:
54 QV4::ReturnedValue evaluate(bool *isUndefined);
55 QV4::PersistentValue m_boundFunction;
56};
57
58class Q_QML_EXPORT QQmlPropertyBinding : public QPropertyBindingPrivate
59
60{
61 friend class QQmlPropertyBindingJS;
62
63 static constexpr std::size_t jsExpressionOffsetLength() {
64 struct composite { QQmlPropertyBinding b; QQmlPropertyBindingJS js; };
65 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
66 return sizeof (QQmlPropertyBinding) - offsetof(composite, js);
67 QT_WARNING_POP
68 }
69
70public:
71
72 QQmlPropertyBindingJS *jsExpression()
73 {
74 return const_cast<QQmlPropertyBindingJS *>(static_cast<const QQmlPropertyBinding *>(this)->jsExpression());
75 }
76
77 QQmlPropertyBindingJS const *jsExpression() const
78 {
79 return std::launder(reinterpret_cast<QQmlPropertyBindingJS const *>(
80 reinterpret_cast<std::byte const*>(this)
81 + QPropertyBindingPrivate::getSizeEnsuringAlignment()
82 + jsExpressionOffsetLength()));
83 }
84
85 static QUntypedPropertyBinding create(const QQmlPropertyData *pd, QV4::Function *function,
86 QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt,
87 QV4::ExecutionContext *scope, QObject *target,
88 QQmlPropertyIndex targetIndex);
89 static QUntypedPropertyBinding create(QMetaType propertyType, QV4::Function *function,
90 QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt,
91 QV4::ExecutionContext *scope, QObject *target,
92 QQmlPropertyIndex targetIndex);
93 static QUntypedPropertyBinding createFromCodeString(const QQmlPropertyData *property,
94 const QString &str, QObject *obj,
95 const QQmlRefPointer<QQmlContextData> &ctxt,
96 const QString &url, quint16 lineNumber,
97 QObject *target, QQmlPropertyIndex targetIndex);
98 static QUntypedPropertyBinding createFromScriptString(const QQmlPropertyData *property,
99 const QQmlScriptString& script, QObject *obj,
100 QQmlContext *ctxt, QObject *target,
101 QQmlPropertyIndex targetIndex);
102
103 static QUntypedPropertyBinding createFromBoundFunction(const QQmlPropertyData *pd, QV4::BoundFunction *function,
104 QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt,
105 QV4::ExecutionContext *scope, QObject *target,
106 QQmlPropertyIndex targetIndex);
107
108 static bool isUndefined(const QUntypedPropertyBinding &binding)
109 {
110 return isUndefined(QPropertyBindingPrivate::get(binding));
111 }
112
113 static bool isUndefined(const QPropertyBindingPrivate *binding)
114 {
115 if (!(binding && binding->hasCustomVTable()))
116 return false;
117 return static_cast<const QQmlPropertyBinding *>(binding)->isUndefined();
118 }
119
120 template<QMetaType::Type type>
121 static bool doEvaluate(QMetaType metaType, QUntypedPropertyData *dataPtr, void *f) {
122 auto address = static_cast<std::byte*>(f);
123 address -= QPropertyBindingPrivate::getSizeEnsuringAlignment(); // f now points to QPropertyBindingPrivate suboject
124 // and that has the same address as QQmlPropertyBinding
125 return reinterpret_cast<QQmlPropertyBinding *>(address)->evaluate<type>(metaType, dataPtr);
126 }
127
128 bool hasDependencies()
129 {
130 return (dependencyObserverCount > 0) || !jsExpression()->activeGuards.isEmpty();
131 }
132
133private:
134 template <QMetaType::Type type>
135 bool evaluate(QMetaType metaType, void *dataPtr);
136
137 Q_NEVER_INLINE void handleUndefinedAssignment(QQmlEnginePrivate *ep, void *dataPtr);
138
139 QString createBindingLoopErrorDescription();
140
141 struct TargetData {
142 enum BoundFunction : bool {
143 WithoutBoundFunction = false,
144 HasBoundFunction = true,
145 };
146 TargetData(QObject *target, QQmlPropertyIndex index, BoundFunction state)
147 : target(target), targetIndex(index), hasBoundFunction(state)
148 {}
149 QObject *target;
150 QQmlPropertyIndex targetIndex;
151 bool hasBoundFunction;
152 bool isUndefined = false;
153 };
154 QQmlPropertyBinding(QMetaType metaType, QObject *target, QQmlPropertyIndex targetIndex, TargetData::BoundFunction hasBoundFunction);
155
156 QObject *target()
157 {
158 return std::launder(reinterpret_cast<TargetData *>(&declarativeExtraData))->target;
159 }
160
161 QQmlPropertyIndex targetIndex()
162 {
163 return std::launder(reinterpret_cast<TargetData *>(&declarativeExtraData))->targetIndex;
164 }
165
166 bool hasBoundFunction()
167 {
168 return std::launder(reinterpret_cast<TargetData *>(&declarativeExtraData))->hasBoundFunction;
169 }
170
171 bool isUndefined() const
172 {
173 return std::launder(reinterpret_cast<TargetData const *>(&declarativeExtraData))->isUndefined;
174 }
175
176 void setIsUndefined(bool isUndefined)
177 {
178 std::launder(reinterpret_cast<TargetData *>(&declarativeExtraData))->isUndefined = isUndefined;
179 }
180
181 static void bindingErrorCallback(QPropertyBindingPrivate *);
182};
183
184template <auto I>
185struct Print {};
186
187namespace QtPrivate {
188template<QMetaType::Type type>
191 [](void *qpropertyBinding){
195 auto address = static_cast<std::byte*>(qpropertyBinding);
196 delete[] address;
197 },
198 [](void *, void *){},
199 0
200};
201}
202
204{
205#define FOR_TYPE(TYPE)
206 case TYPE: return &QtPrivate::bindingFunctionVTableForQQmlPropertyBinding<TYPE>
207 switch (type.id()) {
208 FOR_TYPE(QMetaType::Int);
209 FOR_TYPE(QMetaType::QString);
210 FOR_TYPE(QMetaType::Double);
211 FOR_TYPE(QMetaType::Float);
212 FOR_TYPE(QMetaType::Bool);
213 default:
214 if (type.flags() & QMetaType::PointerToQObject)
215 return &QtPrivate::bindingFunctionVTableForQQmlPropertyBinding<QMetaType::QObjectStar>;
216 return &QtPrivate::bindingFunctionVTableForQQmlPropertyBinding<QMetaType::UnknownType>;
217 }
218#undef FOR_TYPE
219}
220
222{
223public:
224 static QUntypedPropertyBinding Q_QML_EXPORT create(const QQmlPropertyData *pd,
227 static QUntypedPropertyBinding Q_QML_EXPORT
228 create(const QMetaType &pd,
231};
232
233inline const QQmlPropertyBinding *QQmlPropertyBindingJS::asBinding() const
234{
235 return std::launder(reinterpret_cast<QQmlPropertyBinding const *>(
236 reinterpret_cast<std::byte const*>(this)
237 - QPropertyBindingPrivate::getSizeEnsuringAlignment()
238 - QQmlPropertyBinding::jsExpressionOffsetLength()));
239}
240
241static_assert(sizeof(QQmlPropertyBinding) == sizeof(QPropertyBindingPrivate)); // else the whole offset computatation will break
242template<typename T>
243bool compareAndAssign(void *dataPtr, const void *result)
244{
245 if (*static_cast<const T *>(result) == *static_cast<const T *>(dataPtr))
246 return false;
247 *static_cast<T *>(dataPtr) = *static_cast<const T *>(result);
248 return true;
249}
250
251template <QMetaType::Type type>
252bool QQmlPropertyBinding::evaluate(QMetaType metaType, void *dataPtr)
253{
254 const auto ctxt = jsExpression()->context();
255 QQmlEngine *engine = ctxt ? ctxt->engine() : nullptr;
256 if (!engine) {
257 QPropertyBindingError error(QPropertyBindingError::EvaluationError);
258 if (auto currentBinding = QPropertyBindingPrivate::currentlyEvaluatingBinding())
259 currentBinding->setError(std::move(error));
260 return false;
261 }
262 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
263 ep->referenceScarceResources();
264
265 const auto handleErrorAndUndefined = [&](bool evaluatedToUndefined) {
266 ep->dereferenceScarceResources();
267 if (jsExpression()->hasError()) {
268 QPropertyBindingError error(QPropertyBindingError::UnknownError,
269 jsExpression()->delayedError()->error().description());
270 QPropertyBindingPrivate::currentlyEvaluatingBinding()->setError(std::move(error));
271 bindingErrorCallback(this);
272 return false;
273 }
274
275 if (evaluatedToUndefined) {
276 handleUndefinedAssignment(ep, dataPtr);
277 // if property has been changed due to reset, reset is responsible for
278 // notifying observers
279 return false;
280 } else if (isUndefined()) {
281 setIsUndefined(false);
282 }
283
284 return true;
285 };
286
287 if (!hasBoundFunction()) {
288 Q_ASSERT(metaType.sizeOf() > 0);
289
290 using Tuple = std::tuple<qsizetype, bool, bool>;
291 const auto [size, needsConstruction, needsDestruction] = [&]() -> Tuple {
292 switch (type) {
293 case QMetaType::QObjectStar: return Tuple(sizeof(QObject *), false, false);
294 case QMetaType::Bool: return Tuple(sizeof(bool), false, false);
295 case QMetaType::Int: return Tuple(sizeof(int), false, false);
296 case QMetaType::Double: return Tuple(sizeof(double), false, false);
297 case QMetaType::Float: return Tuple(sizeof(float), false, false);
298 case QMetaType::QString: return Tuple(sizeof(QString), true, true);
299 default: {
300 const auto flags = metaType.flags();
301 return Tuple(
302 metaType.sizeOf(),
303 flags & QMetaType::NeedsConstruction,
304 flags & QMetaType::NeedsDestruction);
305 }
306 }
307 }();
308 Q_ALLOCA_VAR(void, result, size);
309 if (needsConstruction)
310 metaType.construct(result);
311
312 const bool evaluatedToUndefined = !jsExpression()->evaluate(&result, &metaType, 0);
313 if (!handleErrorAndUndefined(evaluatedToUndefined))
314 return false;
315
316 switch (type) {
317 case QMetaType::QObjectStar:
318 return compareAndAssign<QObject *>(dataPtr, result);
319 case QMetaType::Bool:
320 return compareAndAssign<bool>(dataPtr, result);
321 case QMetaType::Int:
322 return compareAndAssign<int>(dataPtr, result);
323 case QMetaType::Double:
324 return compareAndAssign<double>(dataPtr, result);
325 case QMetaType::Float:
326 return compareAndAssign<float>(dataPtr, result);
327 case QMetaType::QString: {
328 const bool hasChanged = compareAndAssign<QString>(dataPtr, result);
329 static_cast<QString *>(result)->~QString();
330 return hasChanged;
331 }
332 default:
333 break;
334 }
335
336 const bool hasChanged = !metaType.equals(result, dataPtr);
337 if (hasChanged) {
338 if (needsDestruction)
339 metaType.destruct(dataPtr);
340 metaType.construct(dataPtr, result);
341 }
342 if (needsDestruction)
343 metaType.destruct(result);
344 return hasChanged;
345 }
346
347 bool evaluatedToUndefined = false;
348 QV4::Scope scope(engine->handle());
349 QV4::ScopedValue result(scope, static_cast<QQmlPropertyBindingJSForBoundFunction *>(
350 jsExpression())->evaluate(&evaluatedToUndefined));
351
352 if (!handleErrorAndUndefined(evaluatedToUndefined))
353 return false;
354
355 switch (type) {
356 case QMetaType::Bool: {
357 bool b;
358 if (result->isBoolean())
359 b = result->booleanValue();
360 else
361 b = result->toBoolean();
362 if (b == *static_cast<bool *>(dataPtr))
363 return false;
364 *static_cast<bool *>(dataPtr) = b;
365 return true;
366 }
367 case QMetaType::Int: {
368 int i;
369 if (result->isInteger())
370 i = result->integerValue();
371 else if (result->isNumber()) {
372 i = QV4::StaticValue::toInteger(result->doubleValue());
373 } else {
374 break;
375 }
376 if (i == *static_cast<int *>(dataPtr))
377 return false;
378 *static_cast<int *>(dataPtr) = i;
379 return true;
380 }
381 case QMetaType::Double:
382 if (result->isNumber()) {
383 double d = result->asDouble();
384 if (d == *static_cast<double *>(dataPtr))
385 return false;
386 *static_cast<double *>(dataPtr) = d;
387 return true;
388 }
389 break;
390 case QMetaType::Float:
391 if (result->isNumber()) {
392 float d = float(result->asDouble());
393 if (d == *static_cast<float *>(dataPtr))
394 return false;
395 *static_cast<float *>(dataPtr) = d;
396 return true;
397 }
398 break;
399 case QMetaType::QString:
400 if (result->isString()) {
401 QString s = result->toQStringNoThrow();
402 if (s == *static_cast<QString *>(dataPtr))
403 return false;
404 *static_cast<QString *>(dataPtr) = s;
405 return true;
406 }
407 break;
408 default:
409 break;
410 }
411
412 QVariant resultVariant(QV4::ExecutionEngine::toVariant(result, metaType));
413 resultVariant.convert(metaType);
414 const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
415 metaType.destruct(dataPtr);
416 metaType.construct(dataPtr, resultVariant.constData());
417 return hasChanged;
418}
419
420QT_END_NAMESPACE
421
422#endif // QQMLPROPERTYBINDING_P_H
Combined button and popup list for selecting options.
Definition qjsvalue.h:24
constexpr BindingFunctionVTable bindingFunctionVTableForQQmlPropertyBinding
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static QtPrivate::QPropertyBindingData * bindingDataFromPropertyData(QUntypedPropertyData *dataPtr, QMetaType type)
auto qQmlTranslationPropertyBindingCreateBinding(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, TranslateWithUnit &&translateWithUnit)
#define FOR_TYPE(TYPE)
const QtPrivate::BindingFunctionVTable * bindingFunctionVTableForQQmlPropertyBinding(QMetaType type)
bool compareAndAssign(void *dataPtr, const void *result)