Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
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
56
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
85 QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt,
87 QQmlPropertyIndex targetIndex);
88 static QUntypedPropertyBinding create(QMetaType propertyType, QV4::Function *function,
89 QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt,
91 QQmlPropertyIndex targetIndex);
92 static QUntypedPropertyBinding createFromCodeString(const QQmlPropertyData *property,
93 const QString &str, QObject *obj,
94 const QQmlRefPointer<QQmlContextData> &ctxt,
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,
103 QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt,
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_EXPORT create(const QQmlPropertyData *pd,
224 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
225 const QV4::CompiledData::Binding *binding);
226 static QUntypedPropertyBinding Q_QML_EXPORT
227 create(const QMetaType &pd,
228 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
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());
269 QPropertyBindingPrivate::currentlyEvaluatingBinding()->setError(std::move(error));
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 using Tuple = std::tuple<qsizetype, bool, bool>;
290 const auto [size, needsConstruction, needsDestruction] = [&]() -> Tuple {
291 switch (type) {
292 case QMetaType::QObjectStar: return Tuple(sizeof(QObject *), false, false);
293 case QMetaType::Bool: return Tuple(sizeof(bool), false, false);
294 case QMetaType::Int: return Tuple(sizeof(int), false, false);
295 case QMetaType::Double: return Tuple(sizeof(double), false, false);
296 case QMetaType::Float: return Tuple(sizeof(float), false, false);
297 case QMetaType::QString: return Tuple(sizeof(QString), true, true);
298 default: {
299 const auto flags = metaType.flags();
300 return Tuple(
301 metaType.sizeOf(),
304 }
305 }
306 }();
307 Q_ALLOCA_VAR(void, result, size);
308 if (needsConstruction)
309 metaType.construct(result);
310
311 const bool evaluatedToUndefined = !jsExpression()->evaluate(&result, &metaType, 0);
312 if (!handleErrorAndUndefined(evaluatedToUndefined))
313 return false;
314
315 switch (type) {
316 case QMetaType::QObjectStar:
317 return compareAndAssign<QObject *>(dataPtr, result);
318 case QMetaType::Bool:
319 return compareAndAssign<bool>(dataPtr, result);
320 case QMetaType::Int:
321 return compareAndAssign<int>(dataPtr, result);
322 case QMetaType::Double:
323 return compareAndAssign<double>(dataPtr, result);
324 case QMetaType::Float:
325 return compareAndAssign<float>(dataPtr, result);
326 case QMetaType::QString: {
327 const bool hasChanged = compareAndAssign<QString>(dataPtr, result);
328 static_cast<QString *>(result)->~QString();
329 return hasChanged;
330 }
331 default:
332 break;
333 }
334
335 const bool hasChanged = !metaType.equals(result, dataPtr);
336 if (hasChanged) {
337 if (needsDestruction)
338 metaType.destruct(dataPtr);
339 metaType.construct(dataPtr, result);
340 }
341 if (needsDestruction)
342 metaType.destruct(result);
343 return hasChanged;
344 }
345
346 bool evaluatedToUndefined = false;
347 QV4::Scope scope(engine->handle());
349 jsExpression())->evaluate(&evaluatedToUndefined));
350
351 if (!handleErrorAndUndefined(evaluatedToUndefined))
352 return false;
353
354 switch (type) {
355 case QMetaType::Bool: {
356 bool b;
357 if (result->isBoolean())
358 b = result->booleanValue();
359 else
360 b = result->toBoolean();
361 if (b == *static_cast<bool *>(dataPtr))
362 return false;
363 *static_cast<bool *>(dataPtr) = b;
364 return true;
365 }
366 case QMetaType::Int: {
367 int i;
368 if (result->isInteger())
369 i = result->integerValue();
370 else if (result->isNumber()) {
371 i = QV4::StaticValue::toInteger(result->doubleValue());
372 } else {
373 break;
374 }
375 if (i == *static_cast<int *>(dataPtr))
376 return false;
377 *static_cast<int *>(dataPtr) = i;
378 return true;
379 }
380 case QMetaType::Double:
381 if (result->isNumber()) {
382 double d = result->asDouble();
383 if (d == *static_cast<double *>(dataPtr))
384 return false;
385 *static_cast<double *>(dataPtr) = d;
386 return true;
387 }
388 break;
389 case QMetaType::Float:
390 if (result->isNumber()) {
391 float d = float(result->asDouble());
392 if (d == *static_cast<float *>(dataPtr))
393 return false;
394 *static_cast<float *>(dataPtr) = d;
395 return true;
396 }
397 break;
398 case QMetaType::QString:
399 if (result->isString()) {
400 QString s = result->toQStringNoThrow();
401 if (s == *static_cast<QString *>(dataPtr))
402 return false;
403 *static_cast<QString *>(dataPtr) = s;
404 return true;
405 }
406 break;
407 default:
408 break;
409 }
410
411 QVariant resultVariant(QV4::ExecutionEngine::toVariant(result, metaType));
412 resultVariant.convert(metaType);
413 const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
414 metaType.destruct(dataPtr);
415 metaType.construct(dataPtr, resultVariant.constData());
416 return hasChanged;
417}
418
420
421#endif // QQMLPROPERTYBINDING_P_H
QV4::ExecutionEngine * handle() const
Definition qjsengine.h:298
\inmodule QtCore
Definition qmetatype.h:341
void destruct(void *data) const
constexpr TypeFlags flags() const
Definition qmetatype.h:2658
constexpr qsizetype sizeOf() const
Definition qmetatype.h:2648
bool equals(const void *lhs, const void *rhs) const
Compares the objects at lhs and rhs for equality.
@ NeedsDestruction
Definition qmetatype.h:401
@ PointerToQObject
Definition qmetatype.h:406
@ NeedsConstruction
Definition qmetatype.h:400
void * construct(void *where, const void *copy=nullptr) const
\inmodule QtCore
Definition qobject.h:103
\inmodule QtCore
Definition qproperty.h:131
static QPropertyBindingPrivate * currentlyEvaluatingBinding()
static constexpr size_t getSizeEnsuringAlignment()
bool hasCustomVTable() const
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
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:129
\inmodule QtCore
Definition qvariant.h:65
QString str
[2]
else opt state
[0]
Combined button and popup list for selecting options.
quint64 ReturnedValue
\macro QT_NO_KEYWORDS >
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
GLbitfield flags
GLhandleARB obj
[2]
GLdouble s
[6]
Definition qopenglext.h:235
GLuint GLuint64EXT address
GLuint64EXT * result
[6]
#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:48
#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)