Qt 6.x
The Qt SDK
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
4#ifndef QQMLPROPERTYBINDING_P_H
5#define QQMLPROPERTYBINDING_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/qqmljavascriptexpression_p.h>
19#include <private/qqmlpropertydata_p.h>
20#include <private/qv4alloca_p.h>
21#include <private/qqmltranslation_p.h>
22
23#include <QtCore/qproperty.h>
24
25#include <memory>
26
28
29namespace QV4 {
30 struct BoundFunction;
31}
32
35
36class Q_QML_PRIVATE_EXPORT QQmlPropertyBindingJS : public QQmlJavaScriptExpression
37{
38 bool mustCaptureBindableProperty() const final {return false;}
39
40 friend class QQmlPropertyBinding;
41 void expressionChanged() override;
42 QQmlPropertyBinding *asBinding()
43 {
44 return const_cast<QQmlPropertyBinding *>(static_cast<const QQmlPropertyBindingJS *>(this)->asBinding());
45 }
46
47 inline QQmlPropertyBinding const *asBinding() const;
48};
49
51{
52public:
53 QV4::ReturnedValue evaluate(bool *isUndefined);
55};
56
57class Q_QML_PRIVATE_EXPORT QQmlPropertyBinding : public QPropertyBindingPrivate
58
59{
61
62 static constexpr std::size_t jsExpressionOffsetLength() {
63 struct composite { QQmlPropertyBinding b; QQmlPropertyBindingJS js; };
65 return sizeof (QQmlPropertyBinding) - offsetof(composite, js);
67 }
68
69public:
70
72 {
73 return const_cast<QQmlPropertyBindingJS *>(static_cast<const QQmlPropertyBinding *>(this)->jsExpression());
74 }
75
77 {
78 return std::launder(reinterpret_cast<QQmlPropertyBindingJS const *>(
79 reinterpret_cast<std::byte const*>(this)
81 + jsExpressionOffsetLength()));
82 }
83
87 QQmlPropertyIndex targetIndex);
88 static QUntypedPropertyBinding create(QMetaType propertyType, QV4::Function *function,
91 QQmlPropertyIndex targetIndex);
92 static QUntypedPropertyBinding createFromCodeString(const QQmlPropertyData *property,
93 const QString &str, QObject *obj,
95 const QString &url, quint16 lineNumber,
96 QObject *target, QQmlPropertyIndex targetIndex);
97 static QUntypedPropertyBinding createFromScriptString(const QQmlPropertyData *property,
98 const QQmlScriptString& script, QObject *obj,
100 QQmlPropertyIndex targetIndex);
101
102 static QUntypedPropertyBinding createFromBoundFunction(const QQmlPropertyData *pd, QV4::BoundFunction *function,
105 QQmlPropertyIndex targetIndex);
106
107 static bool isUndefined(const QUntypedPropertyBinding &binding)
108 {
109 return isUndefined(QPropertyBindingPrivate::get(binding));
110 }
111
112 static bool isUndefined(const QPropertyBindingPrivate *binding)
113 {
114 if (!(binding && binding->hasCustomVTable()))
115 return false;
116 return static_cast<const QQmlPropertyBinding *>(binding)->isUndefined();
117 }
118
119 template<QMetaType::Type type>
120 static bool doEvaluate(QMetaType metaType, QUntypedPropertyData *dataPtr, void *f) {
121 auto address = static_cast<std::byte*>(f);
122 address -= QPropertyBindingPrivate::getSizeEnsuringAlignment(); // f now points to QPropertyBindingPrivate suboject
123 // and that has the same address as QQmlPropertyBinding
124 return reinterpret_cast<QQmlPropertyBinding *>(address)->evaluate<type>(metaType, dataPtr);
125 }
126
128 {
129 return (dependencyObserverCount > 0) || !jsExpression()->activeGuards.isEmpty();
130 }
131
132private:
133 template <QMetaType::Type type>
134 bool evaluate(QMetaType metaType, void *dataPtr);
135
136 Q_NEVER_INLINE void handleUndefinedAssignment(QQmlEnginePrivate *ep, void *dataPtr);
137
138 QString createBindingLoopErrorDescription();
139
140 struct TargetData {
141 enum BoundFunction : bool {
142 WithoutBoundFunction = false,
143 HasBoundFunction = true,
144 };
145 TargetData(QObject *target, QQmlPropertyIndex index, BoundFunction state)
146 : target(target), targetIndex(index), hasBoundFunction(state)
147 {}
149 QQmlPropertyIndex targetIndex;
150 bool hasBoundFunction;
151 bool isUndefined = false;
152 };
153 QQmlPropertyBinding(QMetaType metaType, QObject *target, QQmlPropertyIndex targetIndex, TargetData::BoundFunction hasBoundFunction);
154
155 QObject *target()
156 {
157 return std::launder(reinterpret_cast<TargetData *>(&declarativeExtraData))->target;
158 }
159
160 QQmlPropertyIndex targetIndex()
161 {
162 return std::launder(reinterpret_cast<TargetData *>(&declarativeExtraData))->targetIndex;
163 }
164
165 bool hasBoundFunction()
166 {
167 return std::launder(reinterpret_cast<TargetData *>(&declarativeExtraData))->hasBoundFunction;
168 }
169
170 bool isUndefined() const
171 {
172 return std::launder(reinterpret_cast<TargetData const *>(&declarativeExtraData))->isUndefined;
173 }
174
175 void setIsUndefined(bool isUndefined)
176 {
177 std::launder(reinterpret_cast<TargetData *>(&declarativeExtraData))->isUndefined = isUndefined;
178 }
179
180 static void bindingErrorCallback(QPropertyBindingPrivate *);
181};
182
183template <auto I>
184struct Print {};
185
186namespace QtPrivate {
187template<QMetaType::Type type>
189 &QQmlPropertyBinding::doEvaluate<type>,
190 [](void *qpropertyBinding){
191 QQmlPropertyBinding *binding = reinterpret_cast<QQmlPropertyBinding *>(qpropertyBinding);
192 binding->jsExpression()->~QQmlPropertyBindingJS();
193 binding->~QQmlPropertyBinding();
194 auto address = static_cast<std::byte*>(qpropertyBinding);
195 delete[] address;
196 },
197 [](void *, void *){},
198 0
199};
200}
201
203{
204#define FOR_TYPE(TYPE) \
205 case TYPE: return &QtPrivate::bindingFunctionVTableForQQmlPropertyBinding<TYPE>
206 switch (type.id()) {
207 FOR_TYPE(QMetaType::Int);
208 FOR_TYPE(QMetaType::QString);
209 FOR_TYPE(QMetaType::Double);
210 FOR_TYPE(QMetaType::Float);
211 FOR_TYPE(QMetaType::Bool);
212 default:
213 if (type.flags() & QMetaType::PointerToQObject)
214 return &QtPrivate::bindingFunctionVTableForQQmlPropertyBinding<QMetaType::QObjectStar>;
215 return &QtPrivate::bindingFunctionVTableForQQmlPropertyBinding<QMetaType::UnknownType>;
216 }
217#undef FOR_TYPE
218}
219
221{
222public:
223 static QUntypedPropertyBinding Q_QML_PRIVATE_EXPORT create(const QQmlPropertyData *pd,
225 const QV4::CompiledData::Binding *binding);
226 static QUntypedPropertyBinding Q_QML_PRIVATE_EXPORT
227 create(const QMetaType &pd,
229 const QQmlTranslation &translationData);
230};
231
232inline const QQmlPropertyBinding *QQmlPropertyBindingJS::asBinding() const
233{
234 return std::launder(reinterpret_cast<QQmlPropertyBinding const *>(
235 reinterpret_cast<std::byte const*>(this)
237 - QQmlPropertyBinding::jsExpressionOffsetLength()));
238}
239
240static_assert(sizeof(QQmlPropertyBinding) == sizeof(QPropertyBindingPrivate)); // else the whole offset computatation will break
241template<typename T>
242bool compareAndAssign(void *dataPtr, const void *result)
243{
244 if (*static_cast<const T *>(result) == *static_cast<const T *>(dataPtr))
245 return false;
246 *static_cast<T *>(dataPtr) = *static_cast<const T *>(result);
247 return true;
248}
249
250template <QMetaType::Type type>
251bool QQmlPropertyBinding::evaluate(QMetaType metaType, void *dataPtr)
252{
253 const auto ctxt = jsExpression()->context();
254 QQmlEngine *engine = ctxt ? ctxt->engine() : nullptr;
255 if (!engine) {
257 if (auto currentBinding = QPropertyBindingPrivate::currentlyEvaluatingBinding())
258 currentBinding->setError(std::move(error));
259 return false;
260 }
263
264 const auto handleErrorAndUndefined = [&](bool evaluatedToUndefined) {
266 if (jsExpression()->hasError()) {
268 jsExpression()->delayedError()->error().description());
270 bindingErrorCallback(this);
271 return false;
272 }
273
274 if (evaluatedToUndefined) {
275 handleUndefinedAssignment(ep, dataPtr);
276 // if property has been changed due to reset, reset is responsible for
277 // notifying observers
278 return false;
279 } else if (isUndefined()) {
280 setIsUndefined(false);
281 }
282
283 return true;
284 };
285
286 if (!hasBoundFunction()) {
287 Q_ASSERT(metaType.sizeOf() > 0);
288
289 // No need to construct here. evaluate() expects uninitialized memory.
290 const auto size = [&]() -> qsizetype {
291 switch (type) {
292 case QMetaType::QObjectStar: return sizeof(QObject *);
293 case QMetaType::Bool: return sizeof(bool);
294 case QMetaType::Int: return (sizeof(int));
295 case QMetaType::Double: return (sizeof(double));
296 case QMetaType::Float: return (sizeof(float));
297 case QMetaType::QString: return (sizeof(QString));
298 default: return metaType.sizeOf();
299 }
300 }();
301 Q_ALLOCA_VAR(void, result, size);
302
303 const bool evaluatedToUndefined = !jsExpression()->evaluate(&result, &metaType, 0);
304 if (!handleErrorAndUndefined(evaluatedToUndefined))
305 return false;
306
307 switch (type) {
308 case QMetaType::QObjectStar:
309 return compareAndAssign<QObject *>(dataPtr, result);
310 case QMetaType::Bool:
311 return compareAndAssign<bool>(dataPtr, result);
312 case QMetaType::Int:
313 return compareAndAssign<int>(dataPtr, result);
314 case QMetaType::Double:
315 return compareAndAssign<double>(dataPtr, result);
316 case QMetaType::Float:
317 return compareAndAssign<float>(dataPtr, result);
318 case QMetaType::QString: {
319 const bool hasChanged = compareAndAssign<QString>(dataPtr, result);
320 static_cast<QString *>(result)->~QString();
321 return hasChanged;
322 }
323 default:
324 break;
325 }
326
327 const bool hasChanged = !metaType.equals(result, dataPtr);
328 if (hasChanged) {
329 metaType.destruct(dataPtr);
330 metaType.construct(dataPtr, result);
331 }
332 metaType.destruct(result);
333 return hasChanged;
334 }
335
336 bool evaluatedToUndefined = false;
337 QV4::Scope scope(engine->handle());
339 jsExpression())->evaluate(&evaluatedToUndefined));
340
341 if (!handleErrorAndUndefined(evaluatedToUndefined))
342 return false;
343
344 switch (type) {
345 case QMetaType::Bool: {
346 bool b;
347 if (result->isBoolean())
348 b = result->booleanValue();
349 else
350 b = result->toBoolean();
351 if (b == *static_cast<bool *>(dataPtr))
352 return false;
353 *static_cast<bool *>(dataPtr) = b;
354 return true;
355 }
356 case QMetaType::Int: {
357 int i;
358 if (result->isInteger())
359 i = result->integerValue();
360 else if (result->isNumber()) {
361 i = QV4::StaticValue::toInteger(result->doubleValue());
362 } else {
363 break;
364 }
365 if (i == *static_cast<int *>(dataPtr))
366 return false;
367 *static_cast<int *>(dataPtr) = i;
368 return true;
369 }
370 case QMetaType::Double:
371 if (result->isNumber()) {
372 double d = result->asDouble();
373 if (d == *static_cast<double *>(dataPtr))
374 return false;
375 *static_cast<double *>(dataPtr) = d;
376 return true;
377 }
378 break;
379 case QMetaType::Float:
380 if (result->isNumber()) {
381 float d = float(result->asDouble());
382 if (d == *static_cast<float *>(dataPtr))
383 return false;
384 *static_cast<float *>(dataPtr) = d;
385 return true;
386 }
387 break;
388 case QMetaType::QString:
389 if (result->isString()) {
390 QString s = result->toQStringNoThrow();
391 if (s == *static_cast<QString *>(dataPtr))
392 return false;
393 *static_cast<QString *>(dataPtr) = s;
394 return true;
395 }
396 break;
397 default:
398 break;
399 }
400
401 QVariant resultVariant(QV4::ExecutionEngine::toVariant(result, metaType));
402 resultVariant.convert(metaType);
403 const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
404 metaType.destruct(dataPtr);
405 metaType.construct(dataPtr, resultVariant.constData());
406 return hasChanged;
407}
408
410
411#endif // QQMLPROPERTYBINDING_P_H
QV4::ExecutionEngine * handle() const
Definition qjsengine.h:292
\inmodule QtCore
Definition qmetatype.h:320
void destruct(void *data) const
constexpr qsizetype sizeOf() const
Definition qmetatype.h:2618
bool equals(const void *lhs, const void *rhs) const
Compares the objects at lhs and rhs for equality.
@ PointerToQObject
Definition qmetatype.h:385
void * construct(void *where, const void *copy=nullptr) const
\inmodule QtCore
Definition qobject.h:90
\inmodule QtCore
Definition qproperty.h:131
static QPropertyBindingPrivate * currentlyEvaluatingBinding()
static constexpr size_t getSizeEnsuringAlignment()
bool hasCustomVTable() const
void setError(QPropertyBindingError &&e)
static QPropertyBindingPrivate * get(const QUntypedPropertyBinding &binding)
The QQmlContext class defines a context within a QML engine.
Definition qqmlcontext.h:25
static QQmlEnginePrivate * get(QQmlEngine *e)
void referenceScarceResources()
void dereferenceScarceResources()
The QQmlEngine class provides an environment for instantiating QML components.
Definition qqmlengine.h:57
QV4::ReturnedValue evaluate(bool *isUndefined)
QQmlRefPointer< QQmlContextData > context() const
virtual void expressionChanged()=0
bool mustCaptureBindableProperty() const final
static bool doEvaluate(QMetaType metaType, QUntypedPropertyData *dataPtr, void *f)
QQmlPropertyBindingJS const * jsExpression() const
static bool isUndefined(const QPropertyBindingPrivate *binding)
static bool isUndefined(const QUntypedPropertyBinding &binding)
QQmlPropertyBindingJS * jsExpression()
The QQmlScriptString class encapsulates a script and its context.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
\inmodule QtCore
Definition qvariant.h:64
QString str
[2]
else opt state
[0]
Combined button and popup list for selecting options.
\qmltype Particle \inqmlmodule QtQuick.Particles
quint64 ReturnedValue
\macro QT_NAMESPACE
constexpr BindingFunctionVTable bindingFunctionVTableForQQmlPropertyBinding
#define Q_NEVER_INLINE
#define QT_WARNING_POP
#define QT_WARNING_DISABLE_INVALID_OFFSETOF
#define QT_WARNING_PUSH
DBusConnection const char DBusError * error
GLboolean GLboolean GLboolean b
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLfloat GLfloat f
GLenum type
GLenum target
GLhandleARB obj
[2]
GLuint GLuint64EXT address
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
#define FOR_TYPE(TYPE)
const QtPrivate::BindingFunctionVTable * bindingFunctionVTableForQQmlPropertyBinding(QMetaType type)
bool compareAndAssign(void *dataPtr, const void *result)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
unsigned short quint16
Definition qtypes.h:43
ptrdiff_t qsizetype
Definition qtypes.h:70
#define Q_ALLOCA_VAR(type, name, size)
Definition qv4alloca_p.h:36
const char property[13]
Definition qwizard.cpp:101
QUrl url("example.com")
[constructor-url-reference]
view create()
engine evaluate("var myObject = new MyObject()")
[8]
QJSEngine engine
[0]
static QVariant toVariant(const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols=true)
static double toInteger(double d)