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/qqmlpropertybindingbase_p.h>
21#include <private/qqmlpropertydata_p.h>
22#include <private/qv4alloca_p.h>
23#include <private/qqmltranslation_p.h>
24
25#include <QtCore/qproperty.h>
26
27#include <memory>
28
29QT_BEGIN_NAMESPACE
30
31namespace QV4 {
32 struct BoundFunction;
33}
34
35class QQmlPropertyBinding;
36class QQmlScriptString;
37
38class Q_QML_EXPORT QQmlPropertyBindingJS : public QQmlJavaScriptExpression
39{
40 bool mustCaptureBindableProperty() const final {return false;}
41
42 friend class QQmlPropertyBinding;
43 void expressionChanged() override;
44 QQmlPropertyBinding *asBinding()
45 {
46 return const_cast<QQmlPropertyBinding *>(static_cast<const QQmlPropertyBindingJS *>(this)->asBinding());
47 }
48
49 inline QQmlPropertyBinding const *asBinding() const;
50};
51
52class Q_QML_EXPORT QQmlPropertyBindingJSForBoundFunction : public QQmlPropertyBindingJS
53{
54public:
55 QV4::ReturnedValue evaluate(bool *isUndefined);
56 QV4::PersistentValue m_boundFunction;
57};
58
59class Q_QML_EXPORT QQmlPropertyBinding : public QQmlPropertyBindingBase
60
61{
62 friend class QQmlPropertyBindingJS;
63
64 static constexpr std::size_t jsExpressionOffsetLength() {
65 struct composite { QQmlPropertyBinding b; QQmlPropertyBindingJS js; };
66 QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF
67 return sizeof (QQmlPropertyBinding) - offsetof(composite, js);
68 QT_WARNING_POP
69 }
70
71public:
72
73 QQmlPropertyBindingJS *jsExpression()
74 {
75 return const_cast<QQmlPropertyBindingJS *>(static_cast<const QQmlPropertyBinding *>(this)->jsExpression());
76 }
77
78 QQmlPropertyBindingJS const *jsExpression() const
79 {
80 return std::launder(reinterpret_cast<QQmlPropertyBindingJS const *>(
81 reinterpret_cast<std::byte const*>(this)
82 + QPropertyBindingPrivate::getSizeEnsuringAlignment()
83 + jsExpressionOffsetLength()));
84 }
85
86 static QUntypedPropertyBinding create(const QQmlPropertyData *pd, QV4::Function *function,
87 QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt,
88 QV4::ExecutionContext *scope, QObject *target,
89 QQmlPropertyIndex targetIndex);
90 static QUntypedPropertyBinding create(QMetaType propertyType, QV4::Function *function,
91 QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt,
92 QV4::ExecutionContext *scope, QObject *target,
93 QQmlPropertyIndex targetIndex);
94 static QUntypedPropertyBinding createFromCodeString(const QQmlPropertyData *property,
95 const QString &str, QObject *obj,
96 const QQmlRefPointer<QQmlContextData> &ctxt,
97 const QString &url, quint16 lineNumber,
98 QObject *target, QQmlPropertyIndex targetIndex);
99 static QUntypedPropertyBinding createFromScriptString(const QQmlPropertyData *property,
100 const QQmlScriptString& script, QObject *obj,
101 QQmlContext *ctxt, QObject *target,
102 QQmlPropertyIndex targetIndex);
103
104 static QUntypedPropertyBinding createFromBoundFunction(const QQmlPropertyData *pd, QV4::BoundFunction *function,
105 QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt,
106 QV4::ExecutionContext *scope, QObject *target,
107 QQmlPropertyIndex targetIndex);
108
109 static bool isUndefined(const QUntypedPropertyBinding &binding)
110 {
111 return isUndefined(QPropertyBindingPrivate::get(binding));
112 }
113
114 static bool isUndefined(const QPropertyBindingPrivate *binding)
115 {
116 if (!binding || !binding->isQmlBinding())
117 return false;
118 return static_cast<const QQmlPropertyBindingBase *>(binding)->isUndefined();
119 }
120
121 template<QMetaType::Type type>
122 static bool doEvaluate(QMetaType metaType, QUntypedPropertyData *dataPtr, void *f) {
123 auto address = static_cast<std::byte*>(f);
124 address -= QPropertyBindingPrivate::getSizeEnsuringAlignment(); // f now points to QPropertyBindingPrivate suboject
125 // and that has the same address as QQmlPropertyBinding
126 return reinterpret_cast<QQmlPropertyBinding *>(address)->evaluate<type>(metaType, dataPtr);
127 }
128
129 bool hasDependencies() const
130 {
131 return (dependencyObserverCount > 0) || !jsExpression()->activeGuards.isEmpty();
132 }
133
134private:
135 template <QMetaType::Type type>
136 bool evaluate(QMetaType metaType, void *dataPtr);
137
138 Q_NEVER_INLINE void handleUndefinedAssignment(QQmlEnginePrivate *ep, void *dataPtr);
139
140 QString createBindingLoopErrorDescription();
141
142 QQmlPropertyBinding(QMetaType metaType, QObject *target, QQmlPropertyIndex targetIndex, HasBoundFunction hasBoundFunction);
143
144 static QUntypedPropertyBinding makeUntyped(QQmlPropertyBinding *binding)
145 {
146 return QPropertyBindingPrivate::makeUntyped(binding);
147 }
148
149 static void bindingErrorCallback(QPropertyBindingPrivate *);
150};
151
152template <auto I>
153struct Print {};
154
155namespace QtPrivate {
156template<QMetaType::Type type>
159 [](void *qpropertyBinding){
163 auto address = static_cast<std::byte*>(qpropertyBinding);
164 delete[] address;
165 },
166 [](void *, void *){},
167 0
168};
169}
170
172{
173#define FOR_TYPE(TYPE)
174 case TYPE: return &QtPrivate::bindingFunctionVTableForQQmlPropertyBinding<TYPE>
175 switch (type.id()) {
176 FOR_TYPE(QMetaType::Int);
177 FOR_TYPE(QMetaType::QString);
178 FOR_TYPE(QMetaType::Double);
179 FOR_TYPE(QMetaType::Float);
180 FOR_TYPE(QMetaType::Bool);
181 default:
182 if (type.flags() & QMetaType::PointerToQObject)
183 return &QtPrivate::bindingFunctionVTableForQQmlPropertyBinding<QMetaType::QObjectStar>;
184 return &QtPrivate::bindingFunctionVTableForQQmlPropertyBinding<QMetaType::UnknownType>;
185 }
186#undef FOR_TYPE
187}
188
190{
191public:
192 static QUntypedPropertyBinding Q_QML_EXPORT create(const QQmlPropertyData *pd,
195 static QUntypedPropertyBinding Q_QML_EXPORT
196 create(const QMetaType &pd,
199};
200
201inline const QQmlPropertyBinding *QQmlPropertyBindingJS::asBinding() const
202{
203 return std::launder(reinterpret_cast<QQmlPropertyBinding const *>(
204 reinterpret_cast<std::byte const*>(this)
205 - QPropertyBindingPrivate::getSizeEnsuringAlignment()
206 - QQmlPropertyBinding::jsExpressionOffsetLength()));
207}
208
209static_assert(sizeof(QQmlPropertyBinding) == sizeof(QPropertyBindingPrivate)); // else the whole offset computatation will break
210template<typename T>
211bool compareAndAssign(void *dataPtr, const void *result)
212{
213 if (*static_cast<const T *>(result) == *static_cast<const T *>(dataPtr))
214 return false;
215 *static_cast<T *>(dataPtr) = *static_cast<const T *>(result);
216 return true;
217}
218
219template <QMetaType::Type type>
220bool QQmlPropertyBinding::evaluate(QMetaType metaType, void *dataPtr)
221{
222 Q_ALLOCA_INIT();
223 const auto ctxt = jsExpression()->context();
224 QQmlEngine *engine = ctxt ? ctxt->engine() : nullptr;
225 if (!engine) {
226 QPropertyBindingError error(QPropertyBindingError::EvaluationError);
227 if (auto currentBinding = QPropertyBindingPrivate::currentlyEvaluatingBinding())
228 currentBinding->setError(std::move(error));
229 return false;
230 }
231 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
232 ep->referenceScarceResources();
233
234 const auto handleErrorAndUndefined = [&](bool evaluatedToUndefined) {
235 ep->dereferenceScarceResources();
236 if (jsExpression()->hasError()) {
237 QPropertyBindingError error(QPropertyBindingError::UnknownError,
238 jsExpression()->delayedError()->error().description());
239 QPropertyBindingPrivate::currentlyEvaluatingBinding()->setError(std::move(error));
240 bindingErrorCallback(this);
241 return false;
242 }
243
244 if (evaluatedToUndefined) {
245 handleUndefinedAssignment(ep, dataPtr);
246 // if property has been changed due to reset, reset is responsible for
247 // notifying observers
248 return false;
249 } else if (QQmlPropertyBindingBase::isUndefined()) {
250 setIsUndefined(false);
251 }
252
253 return true;
254 };
255
256 if (!hasBoundFunction()) {
257 Q_ASSERT(metaType.sizeOf() > 0);
258
259 using Tuple = std::tuple<qsizetype, bool, bool>;
260 const auto [size, needsConstruction, needsDestruction] = [&]() -> Tuple {
261 switch (type) {
262 case QMetaType::QObjectStar: return Tuple(sizeof(QObject *), false, false);
263 case QMetaType::Bool: return Tuple(sizeof(bool), false, false);
264 case QMetaType::Int: return Tuple(sizeof(int), false, false);
265 case QMetaType::Double: return Tuple(sizeof(double), false, false);
266 case QMetaType::Float: return Tuple(sizeof(float), false, false);
267 case QMetaType::QString: return Tuple(sizeof(QString), true, true);
268 default: {
269 const auto flags = metaType.flags();
270 return Tuple(
271 metaType.sizeOf(),
272 flags & QMetaType::NeedsConstruction,
273 flags & QMetaType::NeedsDestruction);
274 }
275 }
276 }();
277 Q_ALLOCA_VAR(void, result, size);
278 if (needsConstruction)
279 metaType.construct(result);
280
281 const bool evaluatedToUndefined = !jsExpression()->evaluate(&result, &metaType, 0);
282 if (!handleErrorAndUndefined(evaluatedToUndefined))
283 return false;
284
285 switch (type) {
286 case QMetaType::QObjectStar:
287 return compareAndAssign<QObject *>(dataPtr, result);
288 case QMetaType::Bool:
289 return compareAndAssign<bool>(dataPtr, result);
290 case QMetaType::Int:
291 return compareAndAssign<int>(dataPtr, result);
292 case QMetaType::Double:
293 return compareAndAssign<double>(dataPtr, result);
294 case QMetaType::Float:
295 return compareAndAssign<float>(dataPtr, result);
296 case QMetaType::QString: {
297 const bool hasChanged = compareAndAssign<QString>(dataPtr, result);
298 static_cast<QString *>(result)->~QString();
299 return hasChanged;
300 }
301 default:
302 break;
303 }
304
305 const bool hasChanged = !metaType.equals(result, dataPtr);
306 if (hasChanged) {
307 if (needsDestruction)
308 metaType.destruct(dataPtr);
309 metaType.construct(dataPtr, result);
310 }
311 if (needsDestruction)
312 metaType.destruct(result);
313 return hasChanged;
314 }
315
316 bool evaluatedToUndefined = false;
317 QV4::Scope scope(engine->handle());
318 QV4::ScopedValue result(scope, static_cast<QQmlPropertyBindingJSForBoundFunction *>(
319 jsExpression())->evaluate(&evaluatedToUndefined));
320
321 if (!handleErrorAndUndefined(evaluatedToUndefined))
322 return false;
323
324 switch (type) {
325 case QMetaType::Bool: {
326 bool b;
327 if (result->isBoolean())
328 b = result->booleanValue();
329 else
330 b = result->toBoolean();
331 if (b == *static_cast<bool *>(dataPtr))
332 return false;
333 *static_cast<bool *>(dataPtr) = b;
334 return true;
335 }
336 case QMetaType::Int: {
337 int i;
338 if (result->isInteger())
339 i = result->integerValue();
340 else if (result->isNumber()) {
341 i = QV4::StaticValue::toInteger(result->doubleValue());
342 } else {
343 break;
344 }
345 if (i == *static_cast<int *>(dataPtr))
346 return false;
347 *static_cast<int *>(dataPtr) = i;
348 return true;
349 }
350 case QMetaType::Double:
351 if (result->isNumber()) {
352 double d = result->asDouble();
353 if (d == *static_cast<double *>(dataPtr))
354 return false;
355 *static_cast<double *>(dataPtr) = d;
356 return true;
357 }
358 break;
359 case QMetaType::Float:
360 if (result->isNumber()) {
361 float d = float(result->asDouble());
362 if (d == *static_cast<float *>(dataPtr))
363 return false;
364 *static_cast<float *>(dataPtr) = d;
365 return true;
366 }
367 break;
368 case QMetaType::QString:
369 if (result->isString()) {
370 QString s = result->toQStringNoThrow();
371 if (s == *static_cast<QString *>(dataPtr))
372 return false;
373 *static_cast<QString *>(dataPtr) = s;
374 return true;
375 }
376 break;
377 default:
378 break;
379 }
380
381 QVariant resultVariant(QV4::ExecutionEngine::toVariant(result, metaType));
382 resultVariant.convert(metaType);
383 const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
384 metaType.destruct(dataPtr);
385 metaType.construct(dataPtr, resultVariant.constData());
386 return hasChanged;
387}
388
389QT_END_NAMESPACE
390
391#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)