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:
193 {
194 return m_compilationUnit;
195 }
196
197protected:
199 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit)
201 {
202 }
203
204 template <typename TranslateWithUnit>
205 bool translate(QMetaType metaType, void *dataPtr, TranslateWithUnit &&translateWithUnit) const
206 {
207 // Create a dependency to the translationLanguage
208 QQmlEnginePrivate::get(m_compilationUnit->engine)->translationLanguage.value();
209
210 QVariant resultVariant(translateWithUnit());
211 if (metaType != QMetaType::fromType<QString>())
212 resultVariant.convert(metaType);
213
214 const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
215 metaType.destruct(dataPtr);
216 metaType.construct(dataPtr, resultVariant.constData());
217 return hasChanged;
218 }
219
220private:
221 QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compilationUnit;
222};
223
225{
226public:
228 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
229 const QV4::CompiledData::Binding *binding)
231 {
232 }
233
234 bool operator()(QMetaType metaType, void *dataPtr)
235 {
236 return translate(metaType, dataPtr,
237 [this]() { return compilationUnit()->bindingValueAsString(binding); });
238 }
239
240private:
241 const QV4::CompiledData::Binding *binding = nullptr;
242};
243
246{
247public:
249 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
250 const QQmlTranslation &translationData)
253 {
254 }
255
256 bool operator()(QMetaType metaType, void *dataPtr)
257 {
258 return translate(metaType, dataPtr, [this]() { return translationData.translate(); });
259 }
260
261private:
262 const QQmlTranslation translationData;
263};
264
266{
267public:
268 // These must not be inlined: the bindingFunctionVTable<Functor> template variable has internal
269 // linkage under Qt's hidden visibility, so each binary gets its own copy at a distinct address.
270 // Creating a binding and later recovering its compilation unit both compare against that vtable
271 // pointer, so both operations must resolve it in the same shared object (QtQml).
272 static QUntypedPropertyBinding Q_QML_EXPORT
276
277 static QUntypedPropertyBinding Q_QML_EXPORT
281
282 static QQmlRefPointer<QV4::ExecutableCompilationUnit> Q_QML_EXPORT
284};
285
286inline const QQmlPropertyBinding *QQmlPropertyBindingJS::asBinding() const
287{
288 return std::launder(reinterpret_cast<QQmlPropertyBinding const *>(
289 reinterpret_cast<std::byte const*>(this)
290 - QPropertyBindingPrivate::getSizeEnsuringAlignment()
291 - QQmlPropertyBinding::jsExpressionOffsetLength()));
292}
293
294static_assert(sizeof(QQmlPropertyBinding) == sizeof(QPropertyBindingPrivate)); // else the whole offset computatation will break
295template<typename T>
296bool compareAndAssign(void *dataPtr, const void *result)
297{
298 if (*static_cast<const T *>(result) == *static_cast<const T *>(dataPtr))
299 return false;
300 *static_cast<T *>(dataPtr) = *static_cast<const T *>(result);
301 return true;
302}
303
304template <QMetaType::Type type>
305bool QQmlPropertyBinding::evaluate(QMetaType metaType, void *dataPtr)
306{
307 Q_ALLOCA_INIT();
308 const auto ctxt = jsExpression()->context();
309 QQmlEngine *engine = ctxt ? ctxt->engine() : nullptr;
310 if (!engine) {
311 QPropertyBindingError error(QPropertyBindingError::EvaluationError);
312 if (auto currentBinding = QPropertyBindingPrivate::currentlyEvaluatingBinding())
313 currentBinding->setError(std::move(error));
314 return false;
315 }
316 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
317 ep->referenceScarceResources();
318
319 const auto handleErrorAndUndefined = [&](bool evaluatedToUndefined) {
320 ep->dereferenceScarceResources();
321 if (jsExpression()->hasError()) {
322 QPropertyBindingError error(QPropertyBindingError::UnknownError,
323 jsExpression()->delayedError()->error().description());
324 QPropertyBindingPrivate::currentlyEvaluatingBinding()->setError(std::move(error));
325 bindingErrorCallback(this);
326 return false;
327 }
328
329 if (evaluatedToUndefined) {
330 handleUndefinedAssignment(ep, dataPtr);
331 // if property has been changed due to reset, reset is responsible for
332 // notifying observers
333 return false;
334 } else if (QQmlPropertyBindingBase::isUndefined()) {
335 setIsUndefined(false);
336 }
337
338 return true;
339 };
340
341 if (!hasBoundFunction()) {
342 Q_ASSERT(metaType.sizeOf() > 0);
343
344 using Tuple = std::tuple<qsizetype, bool, bool>;
345 const auto [size, needsConstruction, needsDestruction] = [&]() -> Tuple {
346 switch (type) {
347 case QMetaType::QObjectStar: return Tuple(sizeof(QObject *), false, false);
348 case QMetaType::Bool: return Tuple(sizeof(bool), false, false);
349 case QMetaType::Int: return Tuple(sizeof(int), false, false);
350 case QMetaType::Double: return Tuple(sizeof(double), false, false);
351 case QMetaType::Float: return Tuple(sizeof(float), false, false);
352 case QMetaType::QString: return Tuple(sizeof(QString), true, true);
353 default: {
354 const auto flags = metaType.flags();
355 return Tuple(
356 metaType.sizeOf(),
357 flags & QMetaType::NeedsConstruction,
358 flags & QMetaType::NeedsDestruction);
359 }
360 }
361 }();
362 Q_ALLOCA_VAR(void, result, size);
363 if (needsConstruction)
364 metaType.construct(result);
365
366 const bool evaluatedToUndefined = !jsExpression()->evaluate(&result, &metaType, 0);
367 if (!handleErrorAndUndefined(evaluatedToUndefined))
368 return false;
369
370 switch (type) {
371 case QMetaType::QObjectStar:
372 return compareAndAssign<QObject *>(dataPtr, result);
373 case QMetaType::Bool:
374 return compareAndAssign<bool>(dataPtr, result);
375 case QMetaType::Int:
376 return compareAndAssign<int>(dataPtr, result);
377 case QMetaType::Double:
378 return compareAndAssign<double>(dataPtr, result);
379 case QMetaType::Float:
380 return compareAndAssign<float>(dataPtr, result);
381 case QMetaType::QString: {
382 const bool hasChanged = compareAndAssign<QString>(dataPtr, result);
383 static_cast<QString *>(result)->~QString();
384 return hasChanged;
385 }
386 default:
387 break;
388 }
389
390 const bool hasChanged = !metaType.equals(result, dataPtr);
391 if (hasChanged) {
392 if (needsDestruction)
393 metaType.destruct(dataPtr);
394 metaType.construct(dataPtr, result);
395 }
396 if (needsDestruction)
397 metaType.destruct(result);
398 return hasChanged;
399 }
400
401 bool evaluatedToUndefined = false;
402 QV4::Scope scope(engine->handle());
403 QV4::ScopedValue result(scope, static_cast<QQmlPropertyBindingJSForBoundFunction *>(
404 jsExpression())->evaluate(&evaluatedToUndefined));
405
406 if (!handleErrorAndUndefined(evaluatedToUndefined))
407 return false;
408
409 switch (type) {
410 case QMetaType::Bool: {
411 bool b;
412 if (result->isBoolean())
413 b = result->booleanValue();
414 else
415 b = result->toBoolean();
416 if (b == *static_cast<bool *>(dataPtr))
417 return false;
418 *static_cast<bool *>(dataPtr) = b;
419 return true;
420 }
421 case QMetaType::Int: {
422 int i;
423 if (result->isInteger())
424 i = result->integerValue();
425 else if (result->isNumber()) {
426 i = QV4::StaticValue::toInteger(result->doubleValue());
427 } else {
428 break;
429 }
430 if (i == *static_cast<int *>(dataPtr))
431 return false;
432 *static_cast<int *>(dataPtr) = i;
433 return true;
434 }
435 case QMetaType::Double:
436 if (result->isNumber()) {
437 double d = result->asDouble();
438 if (d == *static_cast<double *>(dataPtr))
439 return false;
440 *static_cast<double *>(dataPtr) = d;
441 return true;
442 }
443 break;
444 case QMetaType::Float:
445 if (result->isNumber()) {
446 float d = float(result->asDouble());
447 if (d == *static_cast<float *>(dataPtr))
448 return false;
449 *static_cast<float *>(dataPtr) = d;
450 return true;
451 }
452 break;
453 case QMetaType::QString:
454 if (result->isString()) {
455 QString s = result->toQStringNoThrow();
456 if (s == *static_cast<QString *>(dataPtr))
457 return false;
458 *static_cast<QString *>(dataPtr) = s;
459 return true;
460 }
461 break;
462 default:
463 break;
464 }
465
466 QVariant resultVariant(QV4::ExecutionEngine::toVariant(result, metaType));
467 resultVariant.convert(metaType);
468 const bool hasChanged = !metaType.equals(resultVariant.constData(), dataPtr);
469 metaType.destruct(dataPtr);
470 metaType.construct(dataPtr, resultVariant.constData());
471 return hasChanged;
472}
473
474QT_END_NAMESPACE
475
476#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)
#define FOR_TYPE(TYPE)
const QtPrivate::BindingFunctionVTable * bindingFunctionVTableForQQmlPropertyBinding(QMetaType type)
bool compareAndAssign(void *dataPtr, const void *result)
QQmlTranslationPropertyBindingFunctorBase(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit)
bool translate(QMetaType metaType, void *dataPtr, TranslateWithUnit &&translateWithUnit) const
const QQmlRefPointer< QV4::ExecutableCompilationUnit > & compilationUnit() const
QQmlTranslationPropertyBindingFunctor(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, const QV4::CompiledData::Binding *binding)
bool operator()(QMetaType metaType, void *dataPtr)
bool operator()(QMetaType metaType, void *dataPtr)
QQmlTranslationPropertyTranslationDataFunctor(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, const QQmlTranslation &translationData)