| 1 | // Copyright (C) 2024 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 | #include "qqmlbind_p.h" | 
| 5 |  | 
| 6 | #include <private/qqmlanybinding_p.h> | 
| 7 | #include <private/qqmlbinding_p.h> | 
| 8 | #include <private/qqmlcomponent_p.h> | 
| 9 | #include <private/qqmlmetatype_p.h> | 
| 10 | #include <private/qqmlnullablevalue_p.h> | 
| 11 | #include <private/qqmlproperty_p.h> | 
| 12 | #include <private/qqmlvmemetaobject_p.h> | 
| 13 | #include <private/qv4persistent_p.h> | 
| 14 | #include <private/qv4qmlcontext_p.h> | 
| 15 | #include <private/qv4resolvedtypereference_p.h> | 
| 16 |  | 
| 17 | #include <QtQml/qqmlcontext.h> | 
| 18 | #include <QtQml/qqmlengine.h> | 
| 19 | #include <QtQml/qqmlinfo.h> | 
| 20 | #include <QtQml/qqmlproperty.h> | 
| 21 | #include <QtQml/qqmlpropertymap.h> | 
| 22 |  | 
| 23 | #include <QtCore/private/qobject_p.h> | 
| 24 |  | 
| 25 | #include <QtCore/qdebug.h> | 
| 26 | #include <QtCore/qfile.h> | 
| 27 | #include <QtCore/qloggingcategory.h> | 
| 28 | #include <QtCore/qpointer.h> | 
| 29 | #include <QtCore/qtimer.h> | 
| 30 |  | 
| 31 | QT_BEGIN_NAMESPACE | 
| 32 |  | 
| 33 | Q_LOGGING_CATEGORY(lcQtQmlBindingRemoval, "qt.qml.binding.removal" , QtWarningMsg) | 
| 34 |  | 
| 35 | enum class QQmlBindEntryKind: quint8 { | 
| 36 |     V4Value, | 
| 37 |     Variant, | 
| 38 |     Binding, | 
| 39 |     None | 
| 40 | }; | 
| 41 |  | 
| 42 | /*! | 
| 43 |  * \internal | 
| 44 |  * QQmlBindEntryContent can store one of QV4::Value, QVariant, QQmlAnyBinding, or nothing, | 
| 45 |  * as denoted by QQmlBindEntryKind. It expects the calling code to know what is stored at | 
| 46 |  * any time. On each method invocation, the current kind has to be passed as last parameter | 
| 47 |  * and the new kind is returned. | 
| 48 |  */ | 
| 49 | union QQmlBindEntryContent { | 
| 50 |     Q_DISABLE_COPY_MOVE(QQmlBindEntryContent) | 
| 51 | public: | 
| 52 |     QQmlBindEntryContent() {} | 
| 53 |     ~QQmlBindEntryContent() {} | 
| 54 |  | 
| 55 |     [[nodiscard]] QQmlBindEntryKind set( | 
| 56 |             QQmlBindEntryContent &&other, QQmlBindEntryKind newKind, QQmlBindEntryKind oldKind) | 
| 57 |     { | 
| 58 |         silentDestroy(oldKind); | 
| 59 |         switch (newKind) { | 
| 60 |         case QQmlBindEntryKind::V4Value: | 
| 61 |             new (&v4Value) QV4::PersistentValue(std::move(other.v4Value)); | 
| 62 |             break; | 
| 63 |         case QQmlBindEntryKind::Variant: | 
| 64 |             new (&variant) QVariant(std::move(other.variant)); | 
| 65 |             break; | 
| 66 |         case QQmlBindEntryKind::Binding: | 
| 67 |             new (&binding) QQmlAnyBinding(std::move(other.binding)); | 
| 68 |             break; | 
| 69 |         case QQmlBindEntryKind::None: | 
| 70 |             break; | 
| 71 |         } | 
| 72 |         return newKind; | 
| 73 |     } | 
| 74 |  | 
| 75 |     [[nodiscard]] QQmlBindEntryKind set( | 
| 76 |             const QQmlBindEntryContent &other, QQmlBindEntryKind newKind, QQmlBindEntryKind oldKind) | 
| 77 |     { | 
| 78 |         silentDestroy(oldKind); | 
| 79 |         switch (newKind) { | 
| 80 |         case QQmlBindEntryKind::V4Value: | 
| 81 |             new (&v4Value) QV4::PersistentValue(other.v4Value); | 
| 82 |             break; | 
| 83 |         case QQmlBindEntryKind::Variant: | 
| 84 |             new (&variant) QVariant(other.variant); | 
| 85 |             break; | 
| 86 |         case QQmlBindEntryKind::Binding: | 
| 87 |             new (&binding) QQmlAnyBinding(other.binding); | 
| 88 |             break; | 
| 89 |         case QQmlBindEntryKind::None: | 
| 90 |             break; | 
| 91 |         } | 
| 92 |         return newKind; | 
| 93 |     } | 
| 94 |  | 
| 95 |     [[nodiscard]] QQmlBindEntryKind destroy(QQmlBindEntryKind kind) | 
| 96 |     { | 
| 97 |         switch (kind) { | 
| 98 |         case QQmlBindEntryKind::V4Value: | 
| 99 |             v4Value.~PersistentValue(); | 
| 100 |             break; | 
| 101 |         case QQmlBindEntryKind::Variant: | 
| 102 |             variant.~QVariant(); | 
| 103 |             break; | 
| 104 |         case QQmlBindEntryKind::Binding: | 
| 105 |             binding.~QQmlAnyBinding(); | 
| 106 |             break; | 
| 107 |         case QQmlBindEntryKind::None: | 
| 108 |             break; | 
| 109 |         } | 
| 110 |         return QQmlBindEntryKind::None; | 
| 111 |     } | 
| 112 |  | 
| 113 |     [[nodiscard]] QQmlBindEntryKind set(QVariant v, QQmlBindEntryKind oldKind) | 
| 114 |     { | 
| 115 |         silentDestroy(oldKind); | 
| 116 |         new (&variant) QVariant(std::move(v)); | 
| 117 |         return QQmlBindEntryKind::Variant; | 
| 118 |     } | 
| 119 |  | 
| 120 |     [[nodiscard]] QQmlBindEntryKind set(QV4::PersistentValue v, QQmlBindEntryKind oldKind) | 
| 121 |     { | 
| 122 |         silentDestroy(oldKind); | 
| 123 |         new (&v4Value) QV4::PersistentValue(std::move(v)); | 
| 124 |         return QQmlBindEntryKind::V4Value; | 
| 125 |     } | 
| 126 |  | 
| 127 |     [[nodiscard]] QQmlBindEntryKind set(QQmlAnyBinding v, QQmlBindEntryKind oldKind) | 
| 128 |     { | 
| 129 |         silentDestroy(oldKind); | 
| 130 |         new (&binding) QQmlAnyBinding(std::move(v)); | 
| 131 |         return QQmlBindEntryKind::Binding; | 
| 132 |     } | 
| 133 |  | 
| 134 |     QV4::PersistentValue v4Value; | 
| 135 |     QVariant variant; | 
| 136 |     QQmlAnyBinding binding; | 
| 137 |  | 
| 138 | private: | 
| 139 |     void silentDestroy(QQmlBindEntryKind oldKind) | 
| 140 |     { | 
| 141 |         const QQmlBindEntryKind dead = destroy(kind: oldKind); | 
| 142 |         Q_ASSERT(dead == QQmlBindEntryKind::None); | 
| 143 |         Q_UNUSED(dead); | 
| 144 |     } | 
| 145 | }; | 
| 146 |  | 
| 147 | /*! | 
| 148 |  * \internal | 
| 149 |  * QQmlBindEntry holds two QQmlBindEntryContent members, along with their kinds. | 
| 150 |  * The \l current content is the value or binding the Binding element installs on | 
| 151 |  * the target if enabled (that is, if \l{when}). The \l previous content is what | 
| 152 |  * the target holds before the Binding element installs its binding or value. It | 
| 153 |  * is restored if !\l{when}. The \l prop member holds the target property. | 
| 154 |  */ | 
| 155 | struct QQmlBindEntry | 
| 156 | { | 
| 157 |     QQmlBindEntry() = default; | 
| 158 |     QQmlBindEntry(QQmlBindEntry &&other) noexcept : prop(std::move(other.prop)) | 
| 159 |     { | 
| 160 |         currentKind = current.set(other: std::move(other.current), newKind: other.currentKind, oldKind: currentKind); | 
| 161 |         previousKind = previous.set(other: std::move(other.previous), newKind: other.previousKind, oldKind: previousKind); | 
| 162 |     } | 
| 163 |  | 
| 164 |     QQmlBindEntry(const QQmlBindEntry &other) | 
| 165 |         : prop(other.prop) | 
| 166 |     { | 
| 167 |         currentKind = current.set(other: other.current, newKind: other.currentKind, oldKind: currentKind); | 
| 168 |         previousKind = previous.set(other: other.previous, newKind: other.previousKind, oldKind: previousKind); | 
| 169 |     } | 
| 170 |  | 
| 171 |     ~QQmlBindEntry() | 
| 172 |     { | 
| 173 |         currentKind = current.destroy(kind: currentKind); | 
| 174 |         previousKind = previous.destroy(kind: previousKind); | 
| 175 |     } | 
| 176 |  | 
| 177 |     QQmlBindEntry &operator=(QQmlBindEntry &&other) noexcept | 
| 178 |     { | 
| 179 |         if (this == &other) | 
| 180 |             return *this; | 
| 181 |         prop = std::move(other.prop); | 
| 182 |         currentKind = current.set(other: std::move(other.current), newKind: other.currentKind, oldKind: currentKind); | 
| 183 |         previousKind = previous.set(other: std::move(other.previous), newKind: other.previousKind, oldKind: previousKind); | 
| 184 |         return *this; | 
| 185 |     } | 
| 186 |  | 
| 187 |     QQmlBindEntry &operator=(const QQmlBindEntry &other) | 
| 188 |     { | 
| 189 |         if (this == &other) | 
| 190 |             return *this; | 
| 191 |         prop = other.prop; | 
| 192 |         currentKind = current.set(other: other.current, newKind: other.currentKind, oldKind: currentKind); | 
| 193 |         previousKind = previous.set(other: other.previous, newKind: other.previousKind, oldKind: previousKind); | 
| 194 |         return *this; | 
| 195 |     } | 
| 196 |  | 
| 197 |  | 
| 198 |     QQmlBindEntryContent current; | 
| 199 |     QQmlBindEntryContent previous; | 
| 200 |     QQmlProperty prop; | 
| 201 |     QQmlBindEntryKind currentKind = QQmlBindEntryKind::None; | 
| 202 |     QQmlBindEntryKind previousKind = QQmlBindEntryKind::None; | 
| 203 |  | 
| 204 |     void validate(QQmlBind *q) const; | 
| 205 |     void clearPrev(); | 
| 206 |     void setTarget(QQmlBind *q, const QQmlProperty &p); | 
| 207 | }; | 
| 208 |  | 
| 209 | class QQmlBindPrivate : public QObjectPrivate | 
| 210 | { | 
| 211 | public: | 
| 212 |     QQmlBindPrivate() | 
| 213 |         : when(true) | 
| 214 |         , componentComplete(true) | 
| 215 |         , delayed(false) | 
| 216 |         , pendingEval(false) | 
| 217 |         , restoreBinding(true) | 
| 218 |         , restoreValue(true) | 
| 219 |         , writingProperty(false) | 
| 220 |         , lastIsTarget(false) | 
| 221 |     { | 
| 222 |     } | 
| 223 |     ~QQmlBindPrivate() { } | 
| 224 |  | 
| 225 |     // There can be multiple entries when using the generalized grouped | 
| 226 |     // property syntax. One is used for target/property/value. | 
| 227 |     QVarLengthArray<QQmlBindEntry, 1> entries; | 
| 228 |  | 
| 229 |     // The target object if using the \l target property | 
| 230 |     QPointer<QObject> obj; | 
| 231 |  | 
| 232 |     // Any values we need to create a proxy for. This is necessary when | 
| 233 |     // using the \l delayed member on generalized grouped properties. See | 
| 234 |     // the note on \l delayed. | 
| 235 |     std::unique_ptr<QQmlPropertyMap> delayedValues; | 
| 236 |  | 
| 237 |     // The property name if using the \l property property. | 
| 238 |     QString propName; | 
| 239 |  | 
| 240 |     // Whether the binding is enabled. | 
| 241 |     bool when: 1; | 
| 242 |  | 
| 243 |     // Whether we have already parsed any generalized grouped properties | 
| 244 |     // we might need. | 
| 245 |     bool componentComplete:1; | 
| 246 |  | 
| 247 |     // Whether we should run in "delayed" mode and proxy all values before | 
| 248 |     // applying them to the target. | 
| 249 |     bool delayed:1; | 
| 250 |  | 
| 251 |     // In delayed mode, when using the target/property mode, the \l value | 
| 252 |     // is the proxy. Then pendingEval denotes that a timer is active to | 
| 253 |     // apply the value. We should not start another timer then. | 
| 254 |     bool pendingEval:1; | 
| 255 |  | 
| 256 |     // Whether we should restore bindings on !when. | 
| 257 |     // TODO: Deprecate this and always do. | 
| 258 |     bool restoreBinding:1; | 
| 259 |  | 
| 260 |     // Whether we should restore values on !when. | 
| 261 |     // TODO: Deprecate this and always do. | 
| 262 |     bool restoreValue:1; | 
| 263 |  | 
| 264 |     // writingProperty tracks whether we are updating the target property | 
| 265 |     // when using target/property/value. We use this information to warn about | 
| 266 |     // binding removal if we detect the target property to be updated while we | 
| 267 |     // are not writing it. This doesn't remove the Binding after all. | 
| 268 |     // For generalized grouped properties, we don't have to do this as writing | 
| 269 |     // the target property does remove the binding, just like it removes any | 
| 270 |     // other binding. | 
| 271 |     bool writingProperty:1; | 
| 272 |  | 
| 273 |     // Whether the last entry is the the target property referred to by the | 
| 274 |     // \l target object and the \l property property. This will generally be | 
| 275 |     // the case when using \l target and \l property. | 
| 276 |     bool lastIsTarget:1; | 
| 277 |  | 
| 278 |     QQmlBindEntry *targetEntry(); | 
| 279 |     void validate(QQmlBind *binding) const; | 
| 280 |     void decodeBinding( | 
| 281 |             QQmlBind *q, const QString &propertyPrefix, QQmlData::DeferredData *deferredData, | 
| 282 |             const QV4::CompiledData::Binding *binding, | 
| 283 |             QQmlComponentPrivate::ConstructionState *immediateState); | 
| 284 |     void createDelayedValues(); | 
| 285 |     void onDelayedValueChanged(QString delayedName); | 
| 286 |     void evalDelayed(); | 
| 287 |     void buildBindEntries(QQmlBind *q, QQmlComponentPrivate::DeferredState *deferredState); | 
| 288 | }; | 
| 289 |  | 
| 290 | void QQmlBindEntry::validate(QQmlBind *q) const | 
| 291 | { | 
| 292 |     if (!prop.isWritable()) { | 
| 293 |         qmlWarning(me: q) << "Property '"  << prop.name() << "' on "  | 
| 294 |                       << QQmlMetaType::prettyTypeName(object: prop.object()) << " is read-only." ; | 
| 295 |     } | 
| 296 | } | 
| 297 |  | 
| 298 | QQmlBindEntry *QQmlBindPrivate::targetEntry() | 
| 299 | { | 
| 300 |     if (!lastIsTarget) { | 
| 301 |         entries.append(t: QQmlBindEntry()); | 
| 302 |         lastIsTarget = true; | 
| 303 |     } | 
| 304 |     return &entries.last(); | 
| 305 | } | 
| 306 |  | 
| 307 | void QQmlBindPrivate::validate(QQmlBind *q) const | 
| 308 | { | 
| 309 |     if (!when) | 
| 310 |         return; | 
| 311 |  | 
| 312 |     qsizetype iterationEnd = entries.size(); | 
| 313 |     if (lastIsTarget) { | 
| 314 |         if (obj) { | 
| 315 |             Q_ASSERT(!entries.isEmpty()); | 
| 316 |             const QQmlBindEntry &last = entries.last(); | 
| 317 |             if (!last.prop.isValid()) { | 
| 318 |                 qmlWarning(me: q) << "Property '"  << propName << "' does not exist on "  | 
| 319 |                               << QQmlMetaType::prettyTypeName(object: obj) << "." ; | 
| 320 |                 --iterationEnd; | 
| 321 |             } | 
| 322 |         } else { | 
| 323 |             --iterationEnd; | 
| 324 |         } | 
| 325 |     } | 
| 326 |  | 
| 327 |     for (qsizetype i = 0; i < iterationEnd; ++i) | 
| 328 |         entries[i].validate(q); | 
| 329 | } | 
| 330 |  | 
| 331 | /*! | 
| 332 |     \qmltype Binding | 
| 333 |     \nativetype QQmlBind | 
| 334 |     \inqmlmodule QtQml | 
| 335 |     \ingroup qtquick-interceptors | 
| 336 |     \brief Enables the arbitrary creation of property bindings. | 
| 337 |  | 
| 338 |     In QML, property bindings result in a dependency between the properties of | 
| 339 |     different objects. | 
| 340 |  | 
| 341 |     \section1 Binding to an Inaccessible Property | 
| 342 |  | 
| 343 |     Sometimes it is necessary to bind an object's property to | 
| 344 |     that of another object that isn't directly instantiated by QML, such as a | 
| 345 |     property of a class exported to QML by C++. You can use the Binding type | 
| 346 |     to establish this dependency; binding any value to any object's property. | 
| 347 |  | 
| 348 |     For example, in a C++ application that maps an "app.enteredText" property | 
| 349 |     into QML, you can use Binding to update the enteredText property. | 
| 350 |  | 
| 351 |     \qml | 
| 352 |     TextEdit { id: myTextField; text: "Please type here..." } | 
| 353 |     Binding { app.enteredText: myTextField.text } | 
| 354 |     \endqml | 
| 355 |  | 
| 356 |     When \c{text} changes, the C++ property \c{enteredText} will update | 
| 357 |     automatically. | 
| 358 |  | 
| 359 |     \section1 Conditional Bindings | 
| 360 |  | 
| 361 |     In some cases you may want to modify the value of a property when a certain | 
| 362 |     condition is met but leave it unmodified otherwise. Often, it's not possible | 
| 363 |     to do this with direct bindings, as you have to supply values for all | 
| 364 |     possible branches. | 
| 365 |  | 
| 366 |     For example, the code snippet below results in a warning whenever you | 
| 367 |     release the mouse. This is because the value of the binding is undefined | 
| 368 |     when the mouse isn't pressed. | 
| 369 |  | 
| 370 |     \qml | 
| 371 |     // produces warning: "Unable to assign [undefined] to double value" | 
| 372 |     value: if (mouse.pressed) mouse.mouseX | 
| 373 |     \endqml | 
| 374 |  | 
| 375 |     The Binding type can prevent this warning. | 
| 376 |  | 
| 377 |     \qml | 
| 378 |     Binding on value { | 
| 379 |         when: mouse.pressed | 
| 380 |         value: mouse.mouseX | 
| 381 |     } | 
| 382 |     \endqml | 
| 383 |  | 
| 384 |     The Binding type restores any previously set direct bindings on the | 
| 385 |     property. | 
| 386 |  | 
| 387 |     \sa {Qt Qml} | 
| 388 | */ | 
| 389 | QQmlBind::QQmlBind(QObject *parent) | 
| 390 |     : QObject(*(new QQmlBindPrivate), parent) | 
| 391 | { | 
| 392 | } | 
| 393 |  | 
| 394 | QQmlBind::~QQmlBind() | 
| 395 | { | 
| 396 |     Q_D(QQmlBind); | 
| 397 |     // restore state when dynamic Binding is destroyed | 
| 398 |     if (!(d->when && d->componentComplete && restoreMode() != RestoreNone)) | 
| 399 |         return; | 
| 400 |     // isDeletingChildren is supposed to happen later; we couldn't use declarativeData | 
| 401 |     // if isDeletingChildren were set | 
| 402 |     Q_ASSERT(!d->isDeletingChildren); | 
| 403 |     // We can't use qmlEngine (or QQmlData::get), as that checks for scheduled deletion | 
| 404 |     if (auto ddata = static_cast<QQmlData *>(d->declarativeData); | 
| 405 |         ddata && ddata->context &&  QQmlData::wasDeleted(object: ddata->context->engine())) | 
| 406 |         return; // whole engine is going away; don't bother resetting | 
| 407 |     d->when = false; // internal only change, no signal emission | 
| 408 |     eval(); | 
| 409 | } | 
| 410 |  | 
| 411 | /*! | 
| 412 |     \qmlproperty bool QtQml::Binding::when | 
| 413 |  | 
| 414 |     This property holds when the binding is active. | 
| 415 |     This should be set to an expression that evaluates to true when you want the binding to be active. | 
| 416 |  | 
| 417 |     \qml | 
| 418 |     Binding { | 
| 419 |         contactName.text: name | 
| 420 |         when: list.ListView.isCurrentItem | 
| 421 |     } | 
| 422 |     \endqml | 
| 423 |  | 
| 424 |     By default, any binding or value that was set perviously is restored when the binding becomes | 
| 425 |     inactive. You can customize the restoration behavior using the \l restoreMode property. | 
| 426 |  | 
| 427 |     \sa restoreMode | 
| 428 | */ | 
| 429 | bool QQmlBind::when() const | 
| 430 | { | 
| 431 |     Q_D(const QQmlBind); | 
| 432 |     return d->when; | 
| 433 | } | 
| 434 |  | 
| 435 | void QQmlBind::setWhen(bool v) | 
| 436 | { | 
| 437 |     Q_D(QQmlBind); | 
| 438 |     if (d->when == v) | 
| 439 |         return; | 
| 440 |  | 
| 441 |     d->when = v; | 
| 442 |     if (v && d->componentComplete) | 
| 443 |         d->validate(q: this); | 
| 444 |     eval(); | 
| 445 | } | 
| 446 |  | 
| 447 | /*! | 
| 448 |     \qmlproperty QtObject QtQml::Binding::target | 
| 449 |  | 
| 450 |     The object to be updated. You need to use this property if the binding target | 
| 451 |     does not have an \c id attribute (for example, when the target is a singleton). | 
| 452 |     Otherwise, the following two pieces of code are equivalent: | 
| 453 |  | 
| 454 |     \qml | 
| 455 |     Binding { contactName.text: name } | 
| 456 |     \endqml | 
| 457 |  | 
| 458 |     \qml | 
| 459 |     Binding { | 
| 460 |         target: contactName | 
| 461 |         property: "text" | 
| 462 |         value: name | 
| 463 |     } | 
| 464 |     \endqml | 
| 465 |  | 
| 466 |     The former one is much more compact, but you cannot replace the target | 
| 467 |     object or property at run time. With the latter one you can. | 
| 468 | */ | 
| 469 | QObject *QQmlBind::object() | 
| 470 | { | 
| 471 |     Q_D(const QQmlBind); | 
| 472 |     return d->obj; | 
| 473 | } | 
| 474 |  | 
| 475 | void QQmlBind::setObject(QObject *obj) | 
| 476 | { | 
| 477 |     Q_D(QQmlBind); | 
| 478 |     if (d->obj && d->when) { | 
| 479 |         /* if we switch the object at runtime, we need to restore the | 
| 480 |            previous binding on the old object before continuing */ | 
| 481 |         d->when = false; | 
| 482 |         eval(); | 
| 483 |         d->when = true; | 
| 484 |     } | 
| 485 |     /* if "when" and "target" depend on the same property, we might | 
| 486 |        end up here before we could have updated "when". So reevaluate | 
| 487 |        when manually here. | 
| 488 |      */ | 
| 489 |     const QQmlProperty whenProp(this, QLatin1StringView("when" )); | 
| 490 |     const auto potentialWhenBinding = QQmlAnyBinding::ofProperty(prop: whenProp); | 
| 491 |     if (auto abstractBinding = potentialWhenBinding.asAbstractBinding()) { | 
| 492 |         QQmlBinding *binding = static_cast<QQmlBinding *>(abstractBinding); | 
| 493 |         if (binding->hasValidContext()) { | 
| 494 |             const auto boolType = QMetaType::fromType<bool>(); | 
| 495 |             bool when; | 
| 496 |             binding->evaluate(result: &when, type: boolType); | 
| 497 |             d->when = when; | 
| 498 |         } | 
| 499 |     } | 
| 500 |     d->obj = obj; | 
| 501 |     if (d->componentComplete) { | 
| 502 |         setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this))); | 
| 503 |         if (d->when) | 
| 504 |             d->validate(q: this); | 
| 505 |     } | 
| 506 |     eval(); | 
| 507 | } | 
| 508 |  | 
| 509 | /*! | 
| 510 |     \qmlproperty string QtQml::Binding::property | 
| 511 |  | 
| 512 |     The property to be updated. | 
| 513 |  | 
| 514 |     This can be a group property if the expression results in accessing a | 
| 515 |     property of a \l {QML Value Types}{value type}. For example: | 
| 516 |  | 
| 517 |     \qml | 
| 518 |     Item { | 
| 519 |         id: item | 
| 520 |  | 
| 521 |         property rect rectangle: Qt.rect(0, 0, 200, 200) | 
| 522 |     } | 
| 523 |  | 
| 524 |     Binding { | 
| 525 |         target: item | 
| 526 |         property: "rectangle.x" | 
| 527 |         value: 100 | 
| 528 |     } | 
| 529 |     \endqml | 
| 530 |  | 
| 531 |     You only need to use this property if you can't supply the binding target | 
| 532 |     declaratively. The following snippet of code is equivalent to the above | 
| 533 |     binding, but more compact: | 
| 534 |  | 
| 535 |     \qml | 
| 536 |     Binding { item.rectangle.x: 100 } | 
| 537 |     \endqml | 
| 538 | */ | 
| 539 | QString QQmlBind::property() const | 
| 540 | { | 
| 541 |     Q_D(const QQmlBind); | 
| 542 |     return d->propName; | 
| 543 | } | 
| 544 |  | 
| 545 | void QQmlBind::setProperty(const QString &p) | 
| 546 | { | 
| 547 |     Q_D(QQmlBind); | 
| 548 |     if (!d->propName.isEmpty() && d->when) { | 
| 549 |         /* if we switch the property name at runtime, we need to restore the | 
| 550 |            previous binding on the old object before continuing */ | 
| 551 |         d->when = false; | 
| 552 |         eval(); | 
| 553 |         d->when = true; | 
| 554 |     } | 
| 555 |     d->propName = p; | 
| 556 |     if (d->componentComplete) { | 
| 557 |         setTarget(QQmlProperty(d->obj, d->propName, qmlContext(this))); | 
| 558 |         if (d->when) | 
| 559 |             d->validate(q: this); | 
| 560 |     } | 
| 561 |     eval(); | 
| 562 | } | 
| 563 |  | 
| 564 | /*! | 
| 565 |     \qmlproperty var QtQml::Binding::value | 
| 566 |  | 
| 567 |     The value to be set on the target object and property.  This can be a | 
| 568 |     constant (which isn't very useful), or a bound expression. | 
| 569 |  | 
| 570 |     You only need to use this property if you can't supply the binding target | 
| 571 |     declaratively. Otherwise you can directly bind to the target. | 
| 572 | */ | 
| 573 | QVariant QQmlBind::value() const | 
| 574 | { | 
| 575 |     Q_D(const QQmlBind); | 
| 576 |     if (!d->lastIsTarget) | 
| 577 |         return QVariant(); | 
| 578 |     Q_ASSERT(d->entries.last().currentKind == QQmlBindEntryKind::Variant); | 
| 579 |     return d->entries.last().current.variant; | 
| 580 | } | 
| 581 |  | 
| 582 | void QQmlBind::setValue(const QVariant &v) | 
| 583 | { | 
| 584 |     Q_D(QQmlBind); | 
| 585 |     QQmlBindEntry *targetEntry = d->targetEntry(); | 
| 586 |     targetEntry->currentKind = targetEntry->current.set(v, oldKind: targetEntry->currentKind); | 
| 587 |     prepareEval(); | 
| 588 | } | 
| 589 |  | 
| 590 | /*! | 
| 591 |     \qmlproperty bool QtQml::Binding::delayed | 
| 592 |     \since 5.8 | 
| 593 |  | 
| 594 |     This property holds whether the binding should be delayed. | 
| 595 |  | 
| 596 |     A delayed binding will not immediately update the target, but rather wait | 
| 597 |     until the event queue has been cleared. This can be used as an optimization, | 
| 598 |     or to prevent intermediary values from being assigned. | 
| 599 |  | 
| 600 |     \code | 
| 601 |     Binding { | 
| 602 |         contactName.text.value: givenName + " " + familyName | 
| 603 |         when: list.ListView.isCurrentItem | 
| 604 |         delayed: true | 
| 605 |     } | 
| 606 |     \endcode | 
| 607 |  | 
| 608 |     \note Using the \l delayed property incurs a run time cost as the Binding | 
| 609 |           element has to create a proxy for the value, so that it can delay its | 
| 610 |           application to the actual target. When using the \l target and | 
| 611 |           \l property properties, this cost is lower because the \l value | 
| 612 |           property can be re-used as proxy. When using the form shown above, | 
| 613 |           Binding will allocate a separate object with a dynamic meta-object to | 
| 614 |           hold the proxy values. | 
| 615 | */ | 
| 616 | bool QQmlBind::delayed() const | 
| 617 | { | 
| 618 |     Q_D(const QQmlBind); | 
| 619 |     return d->delayed; | 
| 620 | } | 
| 621 |  | 
| 622 | void QQmlBind::setDelayed(bool delayed) | 
| 623 | { | 
| 624 |     Q_D(QQmlBind); | 
| 625 |     if (d->delayed == delayed) | 
| 626 |         return; | 
| 627 |  | 
| 628 |     d->delayed = delayed; | 
| 629 |     if (!d->componentComplete) | 
| 630 |         return; | 
| 631 |  | 
| 632 |     d->delayedValues.reset(); | 
| 633 |  | 
| 634 |     QVarLengthArray<QQmlBindEntry, 1> oldEntries = std::move(d->entries); | 
| 635 |     d->entries.clear(); | 
| 636 |     d->buildBindEntries(q: this, deferredState: nullptr); | 
| 637 |  | 
| 638 |     if (d->lastIsTarget) { | 
| 639 |         d->entries.append(t: std::move(oldEntries.last())); | 
| 640 |         oldEntries.pop_back(); | 
| 641 |     } | 
| 642 |  | 
| 643 |     for (qsizetype i = 0, end = oldEntries.size(); i < end; ++i) { | 
| 644 |         QQmlBindEntry &newEntry = d->entries[i]; | 
| 645 |         QQmlBindEntry &oldEntry = oldEntries[i]; | 
| 646 |         newEntry.previousKind = newEntry.previous.set( | 
| 647 |                     other: std::move(oldEntry.previous), newKind: oldEntry.previousKind, oldKind: newEntry.previousKind); | 
| 648 |         if (d->delayed && oldEntry.currentKind == QQmlBindEntryKind::Binding) | 
| 649 |             QQmlAnyBinding::removeBindingFrom(prop&: oldEntry.prop); | 
| 650 |     } | 
| 651 |  | 
| 652 |     if (!d->delayed) | 
| 653 |         eval(); | 
| 654 | } | 
| 655 |  | 
| 656 | /*! | 
| 657 |     \qmlproperty enumeration QtQml::Binding::restoreMode | 
| 658 |     \since 5.14 | 
| 659 |  | 
| 660 |     This property can be used to describe if and how the original value should | 
| 661 |     be restored when the binding is disabled. | 
| 662 |  | 
| 663 |     The possible values are: | 
| 664 |  | 
| 665 |     \value Binding.RestoreNone      The original value is not restored at all | 
| 666 |     \value Binding.RestoreBinding   The original value is restored if it was another binding. | 
| 667 |                                     In that case the old binding is in effect again. | 
| 668 |     \value Binding.RestoreValue     The original value is restored if it was a plain | 
| 669 |                                     value rather than a binding. | 
| 670 |     \value Binding.RestoreBindingOrValue The original value is always restored. | 
| 671 |  | 
| 672 |     The default value is \c Binding.RestoreBindingOrValue. | 
| 673 |  | 
| 674 |     \note This property exists for backwards compatibility with earlier versions | 
| 675 |           of Qt. Don't use it in new code. | 
| 676 | */ | 
| 677 | QQmlBind::RestorationMode QQmlBind::restoreMode() const | 
| 678 | { | 
| 679 |     Q_D(const QQmlBind); | 
| 680 |     unsigned result = RestoreNone; | 
| 681 |     if (d->restoreValue) | 
| 682 |         result |= RestoreValue; | 
| 683 |     if (d->restoreBinding) | 
| 684 |         result |= RestoreBinding; | 
| 685 |     return RestorationMode(result); | 
| 686 | } | 
| 687 |  | 
| 688 | void QQmlBind::setRestoreMode(RestorationMode newMode) | 
| 689 | { | 
| 690 |     Q_D(QQmlBind); | 
| 691 |     if (newMode != restoreMode()) { | 
| 692 |         d->restoreValue = (newMode & RestoreValue); | 
| 693 |         d->restoreBinding = (newMode & RestoreBinding); | 
| 694 |         emit restoreModeChanged(); | 
| 695 |     } | 
| 696 | } | 
| 697 |  | 
| 698 | void QQmlBind::setTarget(const QQmlProperty &p) | 
| 699 | { | 
| 700 |     Q_D(QQmlBind); | 
| 701 |     d->targetEntry()->setTarget(q: this, p); | 
| 702 | } | 
| 703 |  | 
| 704 | void QQmlBindEntry::setTarget(QQmlBind *q, const QQmlProperty &p) | 
| 705 | { | 
| 706 |     if (Q_UNLIKELY(lcQtQmlBindingRemoval().isInfoEnabled())) { | 
| 707 |         if (QObject *oldObject = prop.object()) { | 
| 708 |             QMetaProperty metaProp = oldObject->metaObject()->property(index: prop.index()); | 
| 709 |             if (metaProp.hasNotifySignal()) { | 
| 710 |                 QByteArray signal('2' + metaProp.notifySignal().methodSignature()); | 
| 711 |                 QObject::disconnect(sender: oldObject, signal: signal.constData(), | 
| 712 |                                     receiver: q, SLOT(targetValueChanged())); | 
| 713 |             } | 
| 714 |         } | 
| 715 |         p.connectNotifySignal(dest: q, SLOT(targetValueChanged())); | 
| 716 |     } | 
| 717 |  | 
| 718 |     prop = p; | 
| 719 | } | 
| 720 |  | 
| 721 | void QQmlBind::classBegin() | 
| 722 | { | 
| 723 |     Q_D(QQmlBind); | 
| 724 |     d->componentComplete = false; | 
| 725 | } | 
| 726 |  | 
| 727 | static QQmlAnyBinding createBinding( | 
| 728 |         const QQmlProperty &prop, const QV4::CompiledData::Binding *binding, | 
| 729 |         const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, | 
| 730 |         const QQmlRefPointer<QQmlContextData> &contextData, | 
| 731 |         QObject *scopeObject) | 
| 732 | { | 
| 733 |     switch (binding->type()) { | 
| 734 |     case QV4::CompiledData::Binding::Type_Translation: | 
| 735 |     case QV4::CompiledData::Binding::Type_TranslationById: | 
| 736 |         return QQmlAnyBinding::createTranslationBinding(prop, compilationUnit, translationBinding: binding, scopeObject); | 
| 737 |     case QV4::CompiledData::Binding::Type_Script: { | 
| 738 |         const QQmlBinding::Identifier id = binding->value.compiledScriptIndex; | 
| 739 |         if (id == QQmlBinding::Invalid) { | 
| 740 |             return QQmlAnyBinding::createFromCodeString( | 
| 741 |                     prop, code: compilationUnit->bindingValueAsString(binding), obj: scopeObject, | 
| 742 |                     ctxt: contextData, url: compilationUnit->finalUrlString(), lineNumber: binding->location.line()); | 
| 743 |         } | 
| 744 |         QV4::Scope scope(contextData->engine()->handle()); | 
| 745 |         QV4::Scoped<QV4::QmlContext> qmlCtxt( | 
| 746 |                 scope, QV4::QmlContext::create( | 
| 747 |                                parent: scope.engine->rootContext(), context: contextData, scopeObject)); | 
| 748 |         return QQmlAnyBinding::createFromFunction( | 
| 749 |                 prop, function: compilationUnit->runtimeFunctions.at(i: id), obj: scopeObject, ctxt: contextData, | 
| 750 |                 scope: qmlCtxt); | 
| 751 |     } | 
| 752 |     default: | 
| 753 |         break; | 
| 754 |     } | 
| 755 |     return QQmlAnyBinding(); | 
| 756 | } | 
| 757 |  | 
| 758 | static void initCreator( | 
| 759 |         QQmlData::DeferredData *deferredData, | 
| 760 |         const QQmlRefPointer<QQmlContextData> &contextData, | 
| 761 |         QQmlComponentPrivate::ConstructionState  *immediateState) | 
| 762 | { | 
| 763 |     if (!immediateState->hasCreator()) { | 
| 764 |         immediateState->setCompletePending(true); | 
| 765 |         immediateState->initCreator( | 
| 766 |                 parentContext: deferredData->context->parent(), compilationUnit: deferredData->compilationUnit, | 
| 767 |                 creationContext: contextData, inlineComponentName: deferredData->inlineComponentName); | 
| 768 |         immediateState->creator()->beginPopulateDeferred(context: deferredData->context); | 
| 769 |     } | 
| 770 | } | 
| 771 |  | 
| 772 | void QQmlBindPrivate::decodeBinding( | 
| 773 |         QQmlBind *q, const QString &propertyPrefix, | 
| 774 |         QQmlData::DeferredData *deferredData, | 
| 775 |         const QV4::CompiledData::Binding *binding, | 
| 776 |         QQmlComponentPrivate::ConstructionState  *immediateState) | 
| 777 | { | 
| 778 |     const QQmlRefPointer<QV4::ExecutableCompilationUnit> compilationUnit | 
| 779 |             = deferredData->compilationUnit; | 
| 780 |     const QString propertySuffix = compilationUnit->stringAt(index: binding->propertyNameIndex); | 
| 781 |     const QString propertyName = propertyPrefix + propertySuffix; | 
| 782 |  | 
| 783 |     switch (binding->type()) { | 
| 784 |     case QV4::CompiledData::Binding::Type_AttachedProperty: | 
| 785 |         if (propertyPrefix.isEmpty()) { | 
| 786 |             // Top-level attached properties cannot be generalized grouped properties. | 
| 787 |             // Treat them as regular properties. | 
| 788 |             // ... unless we're not supposed to handle regular properties. Then ignore them. | 
| 789 |             if (!immediateState) | 
| 790 |                 return; | 
| 791 |  | 
| 792 |             Q_ASSERT(compilationUnit->stringAt(compilationUnit->objectAt(binding->value.objectIndex) | 
| 793 |                                                        ->inheritedTypeNameIndex).isEmpty()); | 
| 794 |  | 
| 795 |             const QV4::ResolvedTypeReference *typeReference | 
| 796 |                     = compilationUnit->resolvedType(id: binding->propertyNameIndex); | 
| 797 |             Q_ASSERT(typeReference); | 
| 798 |             QQmlType attachedType = typeReference->type(); | 
| 799 |             if (!attachedType.isValid()) { | 
| 800 |                 if (QQmlTypeLoader *typeLoader = compilationUnit->engine->typeLoader()) { | 
| 801 |                     const QQmlTypeNameCache::Result result | 
| 802 |                             = deferredData->context->imports()->query(key: propertySuffix, typeLoader); | 
| 803 |                     if (!result.isValid()) { | 
| 804 |                         qmlWarning(me: q).nospace() | 
| 805 |                                 << "Unknown name "  << propertySuffix << ". The binding is ignored." ; | 
| 806 |                         return; | 
| 807 |                     } | 
| 808 |                     attachedType = result.type; | 
| 809 |                 } | 
| 810 |             } | 
| 811 |  | 
| 812 |             QQmlContext *context = qmlContext(q); | 
| 813 |             QObject *attachedObject = qmlAttachedPropertiesObject( | 
| 814 |                     q, func: attachedType.attachedPropertiesFunction( | 
| 815 |                                engine: QQmlEnginePrivate::get(e: context->engine()))); | 
| 816 |             if (!attachedObject) { | 
| 817 |                 qmlWarning(me: q).nospace() <<"Could not create attached properties object '"  | 
| 818 |                                         << attachedType.typeName() << "'" ; | 
| 819 |                 return; | 
| 820 |             } | 
| 821 |  | 
| 822 |             initCreator(deferredData, contextData: QQmlContextData::get(context), immediateState); | 
| 823 |             immediateState->creator()->populateDeferredInstance( | 
| 824 |                     outerObject: q, deferredIndex: deferredData->deferredIdx, index: binding->value.objectIndex, instance: attachedObject, | 
| 825 |                     bindingTarget: attachedObject, /*value type property*/ valueTypeProperty: nullptr, binding); | 
| 826 |             return; | 
| 827 |         } | 
| 828 |         Q_FALLTHROUGH(); | 
| 829 |     case QV4::CompiledData::Binding::Type_GroupProperty: { | 
| 830 |         const QString pre = propertyName + u'.'; | 
| 831 |         const QV4::CompiledData::Object *subObj | 
| 832 |                 = compilationUnit->objectAt(index: binding->value.objectIndex); | 
| 833 |         const QV4::CompiledData::Binding *subBinding = subObj->bindingTable(); | 
| 834 |         for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) | 
| 835 |             decodeBinding(q, propertyPrefix: pre, deferredData, binding: subBinding, immediateState); | 
| 836 |         return; | 
| 837 |     } | 
| 838 |     default: | 
| 839 |         break; | 
| 840 |     } | 
| 841 |  | 
| 842 |     QQmlBindEntry entry; | 
| 843 |     QQmlContext *context = qmlContext(q); | 
| 844 |     const QQmlRefPointer<QQmlContextData> contextData = QQmlContextData::get(context); | 
| 845 |     entry.prop = QQmlPropertyPrivate::create(target: nullptr, propertyName, context: contextData, | 
| 846 |                                              flags: QQmlPropertyPrivate::InitFlag::AllowId); | 
| 847 |     if (!entry.prop.isValid()) { | 
| 848 |         // Try again in the context of this object. If that works, it's a regular property. | 
| 849 |         // ... unless we're not supposed to handle regular properties. Then ignore it. | 
| 850 |         if (!immediateState) | 
| 851 |             return; | 
| 852 |  | 
| 853 |         QQmlProperty property = QQmlPropertyPrivate::create( | 
| 854 |                     target: q, propertyName, context: contextData, flags: QQmlPropertyPrivate::InitFlag::AllowSignal); | 
| 855 |         if (property.isValid()) { | 
| 856 |             initCreator(deferredData, contextData, immediateState); | 
| 857 |             immediateState->creator()->populateDeferredBinding( | 
| 858 |                         qmlProperty: property, deferredIndex: deferredData->deferredIdx, binding); | 
| 859 |         } else { | 
| 860 |             qmlWarning(me: q).nospace() << "Unknown name "  << propertyName | 
| 861 |                                     << ". The binding is ignored." ; | 
| 862 |         } | 
| 863 |         return; | 
| 864 |     } | 
| 865 |  | 
| 866 |     const auto setVariant = [&entry](QVariant var) { | 
| 867 |         entry.currentKind = entry.current.set(v: std::move(var), oldKind: entry.currentKind); | 
| 868 |     }; | 
| 869 |  | 
| 870 |     const auto setBinding = [&entry](QQmlAnyBinding binding) { | 
| 871 |         entry.currentKind = entry.current.set(v: binding, oldKind: entry.currentKind); | 
| 872 |     }; | 
| 873 |  | 
| 874 |     switch (binding->type()) { | 
| 875 |     case QV4::CompiledData::Binding::Type_AttachedProperty: | 
| 876 |     case QV4::CompiledData::Binding::Type_GroupProperty: | 
| 877 |         Q_UNREACHABLE(); // Handled above | 
| 878 |         break; | 
| 879 |     case QV4::CompiledData::Binding::Type_Translation: | 
| 880 |     case QV4::CompiledData::Binding::Type_TranslationById: | 
| 881 |     case QV4::CompiledData::Binding::Type_Script: | 
| 882 |         if (delayed) { | 
| 883 |             if (!delayedValues) | 
| 884 |                 createDelayedValues(); | 
| 885 |             const QString delayedName = QString::number(entries.size()); | 
| 886 |             delayedValues->insert(key: delayedName, value: QVariant()); | 
| 887 |             QQmlProperty bindingTarget = QQmlProperty(delayedValues.get(), delayedName); | 
| 888 |             Q_ASSERT(bindingTarget.isValid()); | 
| 889 |             QQmlAnyBinding anyBinding = createBinding( | 
| 890 |                     prop: bindingTarget, binding, compilationUnit, contextData, scopeObject: q); | 
| 891 |             anyBinding.installOn(target: bindingTarget); | 
| 892 |         } else { | 
| 893 |             setBinding(createBinding(prop: entry.prop, binding, compilationUnit, contextData, scopeObject: q)); | 
| 894 |         } | 
| 895 |         break; | 
| 896 |     case QV4::CompiledData::Binding::Type_String: | 
| 897 |         setVariant(compilationUnit->bindingValueAsString(binding)); | 
| 898 |         break; | 
| 899 |     case QV4::CompiledData::Binding::Type_Number: | 
| 900 |         setVariant(compilationUnit->bindingValueAsNumber(binding)); | 
| 901 |         break; | 
| 902 |     case QV4::CompiledData::Binding::Type_Boolean: | 
| 903 |         setVariant(binding->valueAsBoolean()); | 
| 904 |         break; | 
| 905 |     case QV4::CompiledData::Binding::Type_Null: | 
| 906 |         setVariant(QVariant::fromValue(value: nullptr)); | 
| 907 |         break; | 
| 908 |     case QV4::CompiledData::Binding::Type_Object: | 
| 909 |     case QV4::CompiledData::Binding::Type_Invalid: | 
| 910 |         break; | 
| 911 |     } | 
| 912 |  | 
| 913 |     entries.append(t: std::move(entry)); | 
| 914 | } | 
| 915 |  | 
| 916 | void QQmlBindPrivate::createDelayedValues() | 
| 917 | { | 
| 918 |     delayedValues = std::make_unique<QQmlPropertyMap>(); | 
| 919 |     QObject::connect( | 
| 920 |             sender: delayedValues.get(), signal: &QQmlPropertyMap::valueChanged, | 
| 921 |             context: delayedValues.get(), slot: [this](QString delayedName, const QVariant &value) { | 
| 922 |                 Q_UNUSED(value); | 
| 923 |                 onDelayedValueChanged(delayedName: std::move(delayedName)); | 
| 924 |             } | 
| 925 |     ); | 
| 926 | } | 
| 927 |  | 
| 928 | void QQmlBindPrivate::onDelayedValueChanged(QString delayedName) | 
| 929 | { | 
| 930 |     Q_ASSERT(delayed); | 
| 931 |     Q_ASSERT(delayedValues); | 
| 932 |     const QString pendingName = QStringLiteral("pending" ); | 
| 933 |     QStringList pending = qvariant_cast<QStringList>(v: (*delayedValues)[pendingName]); | 
| 934 |     if (componentComplete && pending.size() == 0) | 
| 935 |         QTimer::singleShot(interval: 0, receiver: delayedValues.get(), slot: [this]() { evalDelayed(); }); | 
| 936 |     else if (pending.contains(str: delayedName)) | 
| 937 |         return; | 
| 938 |  | 
| 939 |     pending.append(t: std::move(delayedName)); | 
| 940 |     (*delayedValues)[pendingName].setValue(std::move(pending)); | 
| 941 | } | 
| 942 |  | 
| 943 | void QQmlBindPrivate::evalDelayed() | 
| 944 | { | 
| 945 |     if (!when || !delayedValues) | 
| 946 |         return; | 
| 947 |  | 
| 948 |     const QString pendingName = QStringLiteral("pending" ); | 
| 949 |     const QStringList pending = qvariant_cast<QStringList>(v: (*delayedValues)[pendingName]); | 
| 950 |     for (const QString &delayedName : pending) { | 
| 951 |         bool ok; | 
| 952 |         const int delayedIndex = delayedName.toInt(ok: &ok); | 
| 953 |         Q_ASSERT(ok); | 
| 954 |         Q_ASSERT(delayedIndex >= 0 && delayedIndex < entries.size()); | 
| 955 |         entries[delayedIndex].prop.write((*delayedValues)[delayedName]); | 
| 956 |     } | 
| 957 |     (*delayedValues)[pendingName].setValue(QStringList()); | 
| 958 | } | 
| 959 |  | 
| 960 | void QQmlBindPrivate::buildBindEntries(QQmlBind *q, QQmlComponentPrivate::DeferredState *deferredState) | 
| 961 | { | 
| 962 |     QQmlData *data = QQmlData::get(object: q); | 
| 963 |     if (data && !data->deferredData.isEmpty()) { | 
| 964 |         QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: data->context->engine()); | 
| 965 |         for (QQmlData::DeferredData *deferredData : data->deferredData) { | 
| 966 |             QMultiHash<int, const QV4::CompiledData::Binding *> *bindings = &deferredData->bindings; | 
| 967 |             if (deferredState) { | 
| 968 |                 QQmlComponentPrivate::ConstructionState constructionState; | 
| 969 |                 for (auto it = bindings->cbegin(); it != bindings->cend(); ++it) | 
| 970 |                     decodeBinding(q, propertyPrefix: QString(), deferredData, binding: *it, immediateState: &constructionState); | 
| 971 |  | 
| 972 |  | 
| 973 |                 if (constructionState.hasCreator()) { | 
| 974 |                     ++ep->inProgressCreations; | 
| 975 |                     constructionState.creator()->finalizePopulateDeferred(); | 
| 976 |                     constructionState.appendCreatorErrors(); | 
| 977 |                     deferredState->push_back(x: std::move(constructionState)); | 
| 978 |                 } | 
| 979 |             } else { | 
| 980 |                 for (auto it = bindings->cbegin(); it != bindings->cend(); ++it) | 
| 981 |                     decodeBinding(q, propertyPrefix: QString(), deferredData, binding: *it, immediateState: nullptr); | 
| 982 |             } | 
| 983 |         } | 
| 984 |  | 
| 985 |         if (deferredState) { | 
| 986 |             data->releaseDeferredData(); | 
| 987 |             if (!deferredState->empty()) | 
| 988 |                 QQmlComponentPrivate::completeDeferred(enginePriv: ep, deferredState); | 
| 989 |         } | 
| 990 |     } | 
| 991 | } | 
| 992 |  | 
| 993 | void QQmlBind::componentComplete() | 
| 994 | { | 
| 995 |     Q_D(QQmlBind); | 
| 996 |     QQmlComponentPrivate::DeferredState deferredState; | 
| 997 |     d->buildBindEntries(q: this, deferredState: &deferredState); | 
| 998 |     d->componentComplete = true; | 
| 999 |     if (!d->propName.isEmpty() || d->obj) { | 
| 1000 |         QQmlBindEntry *target = d->targetEntry(); | 
| 1001 |         if (!target->prop.isValid()) | 
| 1002 |             target->setTarget(q: this, p: QQmlProperty(d->obj, d->propName, qmlContext(this))); | 
| 1003 |     } | 
| 1004 |     d->validate(q: this); | 
| 1005 |     d->evalDelayed(); | 
| 1006 |     eval(); | 
| 1007 | } | 
| 1008 |  | 
| 1009 | void QQmlBind::prepareEval() | 
| 1010 | { | 
| 1011 |     Q_D(QQmlBind); | 
| 1012 |     if (d->delayed) { | 
| 1013 |         if (!d->pendingEval) | 
| 1014 |             QTimer::singleShot(interval: 0, receiver: this, slot: &QQmlBind::eval); | 
| 1015 |         d->pendingEval = true; | 
| 1016 |     } else { | 
| 1017 |         eval(); | 
| 1018 |     } | 
| 1019 | } | 
| 1020 |  | 
| 1021 | void QQmlBindEntry::clearPrev() | 
| 1022 | { | 
| 1023 |     previousKind = previous.destroy(kind: previousKind); | 
| 1024 | } | 
| 1025 |  | 
| 1026 | void QQmlBind::eval() | 
| 1027 | { | 
| 1028 |     Q_D(QQmlBind); | 
| 1029 |     d->pendingEval = false; | 
| 1030 |     if (!d->componentComplete) | 
| 1031 |         return; | 
| 1032 |  | 
| 1033 |     for (QQmlBindEntry &entry : d->entries) { | 
| 1034 |         if (!entry.prop.isValid() || (entry.currentKind == QQmlBindEntryKind::None)) | 
| 1035 |             continue; | 
| 1036 |         if (!entry.prop.object()) | 
| 1037 |             continue; // if the target is already gone, we can't do anything | 
| 1038 |  | 
| 1039 |         if (!d->when) { | 
| 1040 |             //restore any previous binding | 
| 1041 |             switch (entry.previousKind) { | 
| 1042 |             case QQmlBindEntryKind::Binding: | 
| 1043 |                 if (d->restoreBinding) { | 
| 1044 |                     QQmlAnyBinding p = std::move(entry.previous.binding); | 
| 1045 |                     entry.clearPrev(); // Do that before setBinding(), as setBinding() may recurse. | 
| 1046 |                     p.installOn(target: entry.prop); | 
| 1047 |                 } | 
| 1048 |                 break; | 
| 1049 |             case QQmlBindEntryKind::V4Value: | 
| 1050 |                 if (d->restoreValue) { | 
| 1051 |                     QQmlAnyBinding::takeFrom(prop: entry.prop); // we don't want to have a binding active | 
| 1052 |                     auto propPriv = QQmlPropertyPrivate::get(p: entry.prop); | 
| 1053 |                     QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: propPriv->object); | 
| 1054 |                     Q_ASSERT(vmemo); | 
| 1055 |                     vmemo->setVMEProperty(index: propPriv->core.coreIndex(), | 
| 1056 |                                           v: *entry.previous.v4Value.valueRef()); | 
| 1057 |                     entry.clearPrev(); | 
| 1058 |                 } | 
| 1059 |                 break; | 
| 1060 |             case QQmlBindEntryKind::Variant: | 
| 1061 |                 if (d->restoreValue) { | 
| 1062 |                     QQmlAnyBinding::takeFrom(prop: entry.prop); // we don't want to have a binding active | 
| 1063 |                     entry.prop.write(entry.previous.variant); | 
| 1064 |                     entry.clearPrev(); | 
| 1065 |                 } | 
| 1066 |                 break; | 
| 1067 |             case QQmlBindEntryKind::None: | 
| 1068 |                 break; | 
| 1069 |             } | 
| 1070 |             continue; | 
| 1071 |         } | 
| 1072 |  | 
| 1073 |         //save any set binding for restoration | 
| 1074 |         if (entry.previousKind == QQmlBindEntryKind::None) { | 
| 1075 |             // try binding first; we need to use takeFrom to properly unlink the binding | 
| 1076 |             QQmlAnyBinding prevBind = QQmlAnyBinding::takeFrom(prop: entry.prop); | 
| 1077 |             if (prevBind) { | 
| 1078 |                 entry.previousKind = entry.previous.set(v: std::move(prevBind), oldKind: entry.previousKind); | 
| 1079 |             } else { | 
| 1080 |                 // nope, try a V4 value next | 
| 1081 |                 auto propPriv = QQmlPropertyPrivate::get(p: entry.prop); | 
| 1082 |                 auto propData = propPriv->core; | 
| 1083 |                 if (!propPriv->valueTypeData.isValid() && propData.isVarProperty()) { | 
| 1084 |                     QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(obj: propPriv->object); | 
| 1085 |                     Q_ASSERT(vmemo); | 
| 1086 |                     auto retVal = vmemo->vmeProperty(index: propData.coreIndex()); | 
| 1087 |                     entry.previousKind = entry.previous.set( | 
| 1088 |                                 v: QV4::PersistentValue(vmemo->engine, retVal), oldKind: entry.previousKind); | 
| 1089 |                 } else { | 
| 1090 |                     // nope, use the meta object to get a QVariant | 
| 1091 |                     entry.previousKind = entry.previous.set(v: entry.prop.read(), oldKind: entry.previousKind); | 
| 1092 |                 } | 
| 1093 |             } | 
| 1094 |         } | 
| 1095 |  | 
| 1096 |         // NOTE: removeBinding has no effect on QProperty classes, but | 
| 1097 |         // we already used takeBinding to remove it | 
| 1098 |         QQmlPropertyPrivate::removeBinding(that: entry.prop); | 
| 1099 |     } | 
| 1100 |  | 
| 1101 |     if (!d->when) | 
| 1102 |         return; | 
| 1103 |  | 
| 1104 |     d->writingProperty = true; | 
| 1105 |     for (qsizetype i = 0, end = d->entries.size(); i != end; ++i) { | 
| 1106 |         QQmlBindEntry &entry = d->entries[i]; | 
| 1107 |         if (!entry.prop.isValid()) | 
| 1108 |             continue; | 
| 1109 |         switch (entry.currentKind) { | 
| 1110 |         case QQmlBindEntryKind::Variant: | 
| 1111 |             entry.prop.write(entry.current.variant); | 
| 1112 |             break; | 
| 1113 |         case QQmlBindEntryKind::Binding: | 
| 1114 |             Q_ASSERT(!d->delayed); | 
| 1115 |             entry.current.binding.installOn(target: entry.prop); | 
| 1116 |             break; | 
| 1117 |         case QQmlBindEntryKind::V4Value: { | 
| 1118 |             auto propPriv = QQmlPropertyPrivate::get(p: entry.prop); | 
| 1119 |             QQmlVMEMetaObject::get(obj: propPriv->object)->setVMEProperty( | 
| 1120 |                         index: propPriv->core.coreIndex(), v: *entry.current.v4Value.valueRef()); | 
| 1121 |             break; | 
| 1122 |         } | 
| 1123 |         case QQmlBindEntryKind::None: | 
| 1124 |             break; | 
| 1125 |         } | 
| 1126 |     } | 
| 1127 |     d->writingProperty = false; | 
| 1128 | } | 
| 1129 |  | 
| 1130 | void QQmlBind::targetValueChanged() | 
| 1131 | { | 
| 1132 |     Q_D(QQmlBind); | 
| 1133 |     if (d->writingProperty) | 
| 1134 |         return; | 
| 1135 |  | 
| 1136 |     if (!d->when) | 
| 1137 |         return; | 
| 1138 |  | 
| 1139 |     QUrl url; | 
| 1140 |     quint16 line = 0; | 
| 1141 |  | 
| 1142 |     const QQmlData *ddata = QQmlData::get(object: this, create: false); | 
| 1143 |     if (ddata && ddata->outerContext) { | 
| 1144 |         url = ddata->outerContext->url(); | 
| 1145 |         line = ddata->lineNumber; | 
| 1146 |     } | 
| 1147 |  | 
| 1148 |     qCInfo(lcQtQmlBindingRemoval, | 
| 1149 |            "The target property of the Binding element created at %s:%d was changed from "  | 
| 1150 |            "elsewhere. This does not overwrite the binding. The target property will still be "  | 
| 1151 |            "updated when the value of the Binding element changes." , | 
| 1152 |            qPrintable(url.toString()), line); | 
| 1153 | } | 
| 1154 |  | 
| 1155 | QT_END_NAMESPACE | 
| 1156 |  | 
| 1157 | #include "moc_qqmlbind_p.cpp" | 
| 1158 |  |