7#include <private/qqmlcomponent_p.h>
8#include <private/qqmlnotifier_p.h>
9#include <private/qqmlobjectcreator_p.h>
10#include <private/qqmlproperty_p.h>
11#include <private/qqmlproperty_p.h>
12#include <private/qqmlpropertybinding_p.h>
13#include <private/qqmlpropertytopropertybinding_p.h>
14#include <private/qqmltypeloader_p.h>
15#include <private/qqmlvme_p.h>
16#include <private/qv4functionobject_p.h>
17#include <private/qv4generatorobject_p.h>
18#include <private/qv4qmlcontext_p.h>
19#include <private/qv4resolvedtypereference_p.h>
21#include <QtCore/qqueue.h>
22#include <QtCore/qset.h>
30 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &cu,
33 if (f->executableCompilationUnit() != cu)
36 const QV4::CompiledData::Object *obj = cu->objectAt(objectIndex);
37 for (
auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) {
38 switch (binding->type()) {
39 case QV4::CompiledData::Binding::Type_GroupProperty:
40 case QV4::CompiledData::Binding::Type_AttachedProperty:
41 case QV4::CompiledData::Binding::Type_Object:
42 if (functionBelongsToObject(f, cu, binding->value.objectIndex))
45 case QV4::CompiledData::Binding::Type_Script:
46 if (cu->runtimeFunctions[binding->value.compiledScriptIndex] == f)
60 const std::vector<CompositeLevel> &internalUnits,
66 const QV4::Function *f =
nullptr;
68 if (
const QQmlAbstractBinding *abstractBinding = binding.asAbstractBinding()) {
70 if (abstractBinding->kind() == QQmlAbstractBinding::QmlBinding)
71 f =
static_cast<
const QQmlBinding *>(abstractBinding)->function();
72 }
else if (
const QPropertyBindingPrivate *priv =
73 QPropertyBindingPrivate::get(binding.asUntypedPropertyBinding());
74 priv && priv->isQmlBinding()) {
78 const auto base =
static_cast<
const QQmlPropertyBindingBase *>(priv);
79 if (base->bindingKind() == QQmlPropertyBindingBase::BindingKind::JavaScript) {
80 if (
const QQmlPropertyBindingJS *jsExpr =
81 static_cast<
const QQmlPropertyBinding *>(base)->jsExpression()) {
82 f = jsExpr->function();
90 for (
const auto &internalUnit : internalUnits) {
91 if (functionBelongsToObject(f, internalUnit.oldCu, internalUnit.objectIndex)
92 || functionBelongsToObject(f, internalUnit.newCu, internalUnit.objectIndex)) {
97 const QQmlData *ddata = QQmlData::get(target);
104 for (QQmlRefPointer<QQmlContextData> context = ddata->outerContext; context;
105 context = context->parent()) {
106 const QQmlRefPointer<QV4::ExecutableCompilationUnit> cu = context->typeCompilationUnit();
110 if (f->executableCompilationUnit() == cu)
113 if (std::any_of(internalUnits.begin(), internalUnits.end(),
114 [&](
const CompositeLevel &level) {
115 return level.oldCu == cu || level.newCu == cu;
126 if (
const QQmlAbstractBinding *abstractBinding = binding.asAbstractBinding()) {
127 if (abstractBinding->kind() == QQmlAbstractBinding::PropertyToPropertyBinding) {
128 return static_cast<
const QQmlPropertyToUnbindablePropertyBinding *>(abstractBinding)
131 }
else if (
const QPropertyBindingPrivate *priv =
132 QPropertyBindingPrivate::get(binding.asUntypedPropertyBinding());
133 priv && priv->isQmlBinding()) {
134 const auto base =
static_cast<
const QQmlPropertyBindingBase *>(priv);
135 if (base->bindingKind() == QQmlPropertyBindingBase::BindingKind::PropertyToProperty)
136 return static_cast<
const QQmlPropertyToBindablePropertyBinding *>(priv)->source();
142 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
int cuIndex,
143 QHash<QString, QVariant> *constantValues, QDuplicateTracker<QObject *> *seenChildren)
145 Q_ASSERT(constantValues);
147 if (!unit || cuIndex >= unit->objectCount())
150 const QV4::CompiledData::Object *obj = unit->objectAt(cuIndex);
151 const QQmlPropertyCache::ConstPtr cache = unit->propertyCachesPtr()->at(cuIndex);
152 const QString defaultPropertyName = cache ? cache->defaultPropertyName() : QString();
154 for (
auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) {
156 const QString name = binding->propertyNameIndex == 0
157 ? defaultPropertyName
158 : unit->stringAt(binding->propertyNameIndex);
162 switch (binding->type()) {
163 case QV4::CompiledData::Binding::Type_AttachedProperty:
164 attachedContext(unit, binding, seenChildren);
166 case QV4::CompiledData::Binding::Type_GroupProperty:
167 childContext(unit, binding, seenChildren);
173 if (binding->isSignalHandler())
176 if (binding->hasFlag(QV4::CompiledData::Binding::IsCustomParserBinding))
179 const qsizetype size = constantValues->size();
180 QVariant &value = (*constantValues)[name];
181 if (constantValues->size() == size)
185 switch (binding->type()) {
186 case QV4::CompiledData::Binding::Type_Number: {
187 const double d = unit->bindingValueAsNumber(binding);
188 value = QV4::Value::isInt32(d) ? QVariant(
int(d)) : QVariant(d);
191 case QV4::CompiledData::Binding::Type_Boolean:
192 value = QVariant(
bool(binding->value.b));
194 case QV4::CompiledData::Binding::Type_Translation:
195 case QV4::CompiledData::Binding::Type_TranslationById:
196 case QV4::CompiledData::Binding::Type_String:
197 value = unit->bindingValueAsString(binding);
199 case QV4::CompiledData::Binding::Type_Null:
200 value = QVariant::fromValue(
nullptr);
210 for (
int propertyIndex = 0, end = obj->propertyCount(); propertyIndex != end; ++propertyIndex) {
211 const qsizetype size = constantValues->size();
213 (*constantValues)[unit->stringAt(obj->propertyTable()[propertyIndex].nameIndex())];
214 if (constantValues->size() != size)
215 value = QVariant(cache->property(cache->propertyOffset() + propertyIndex)->propType());
220 QDuplicateTracker<QObject *> *seenChildren)
223 QHash<QString, QVariant> constantValues;
224 recordBindingValues(unit, objectIndex, &constantValues, seenChildren);
226 if (prefix.isEmpty() && QQmlData::get(m_object)->hasVMEMetaObject) {
227 for (QQmlVMEMetaObject *vmeMeta =
228 static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(m_object)->metaObject);
229 vmeMeta; vmeMeta = vmeMeta->parentVMEMetaObject()) {
230 if (
auto cu = vmeMeta->compilationUnit())
231 recordBindingValues(cu, vmeMeta->qmlObjectId(), &constantValues, seenChildren);
240 const QMetaObject *mo = m_object->metaObject();
241 for (
int i = 0, count = mo->propertyCount(); i < count; ++i) {
242 const QMetaProperty metaProp = mo->property(i);
243 const QString propName = QString::fromUtf8(metaProp.name());
245 const QQmlProperty qProp(m_object, propName);
246 if (!qProp.isValid())
252 if (qProp.propertyMetaType().flags().testFlag(QMetaType::PointerToQObject)) {
253 if (QObject *child = qProp.read().value<QObject *>()) {
254 if (QQmlData *childDdata = QQmlData::get(child)) {
255 if (
const auto &childCU = childDdata->compilationUnit; childCU
256 && std::find_if(internalUnits.begin(), internalUnits.end(),
257 [&](
const CompositeLevel &level) {
258 return level.newCu == childCU || level.oldCu == childCU;
260 != internalUnits.end()) {
261 childContext(propName, child, childCU, childDdata->cuObjectIndex,
268 const auto it = constantValues.constFind(propName);
269 if (it == constantValues.cend()) {
271 const QQmlAnyBinding binding = QQmlAnyBinding::ofProperty(qProp);
272 if (isExternalBinding(binding, internalUnits, m_object)) {
273 QQmlAnyBinding taken = QQmlAnyBinding::takeFrom(qProp);
274 m_storedBindings.push_back(
275 { propName, std::move(taken), propertyToPropertySource(binding) });
281 const QQmlAnyBinding binding = QQmlAnyBinding::ofProperty(qProp);
282 if (isExternalBinding(binding, internalUnits, m_object)) {
283 QQmlAnyBinding taken = QQmlAnyBinding::takeFrom(qProp);
284 m_storedBindings.push_back(
285 { propName, std::move(taken), propertyToPropertySource(binding) });
294 if (!it->isValid()) {
304 const QMetaType expectedMetaType = qProp.propertyMetaType();
306 if (expectedMetaType != QMetaType::fromType<QVariant>()
307 && it->metaType() != expectedMetaType) {
308 QV4::ExecutionEngine *v4 = unit->engine;
309 QV4::Scope scope(v4);
310 QV4::ScopedValue v(scope, v4->metaTypeToJS(it->metaType(), it->constData()));
311 expected = QVariant(expectedMetaType);
312 v4->metaTypeFromJS(v, expectedMetaType, expected.data());
317 if (
const QVariant current = qProp.read(); current != expected)
318 m_storedValues.push_back({ propName, current });
321 const auto stashBoundSignal = [&](QQmlBoundSignal *boundSignal) {
322 const QByteArray signature =
323 QMetaObjectPrivate::signal(m_object->metaObject(), boundSignal->signalIndex())
325 QQmlNotifierEndpoint *next = boundSignal->nextEndpoint();
326 boundSignal->disconnect();
327 m_storedSignalHandlers.push_back(
328 { QString::fromUtf8(signature), std::unique_ptr<QQmlBoundSignal>(boundSignal) });
337 if (QQmlNotifyList *list = QQmlData::get(m_object)->notifyList.loadRelaxed()) {
344 for (quint16 i = 0, end = list->notifiesSize; i < end; ++i) {
345 for (QQmlNotifierEndpoint *ep = list->notifies[i]; ep;) {
346 if (ep->callbackType() != QQmlNotifierEndpoint::QQmlBoundSignal) {
347 ep = ep->nextEndpoint();
351 QQmlBoundSignal *boundSignal =
static_cast<QQmlBoundSignal *>(ep);
352 QQmlBoundSignalExpression *expr = boundSignal->expression();
354 ep = stashBoundSignal(boundSignal);
358 const QV4::Function *f = expr->function();
360 ep = stashBoundSignal(boundSignal);
364 bool isInternal =
false;
365 for (
const CompositeLevel &internalUnit : internalUnits) {
366 if (functionBelongsToObject(f, internalUnit.oldCu, internalUnit.objectIndex)
367 || functionBelongsToObject(f, internalUnit.newCu,
368 internalUnit.objectIndex)) {
374 ep = isInternal ? ep->nextEndpoint() : stashBoundSignal(boundSignal);
380 for (
auto &[name, child] : m_children) {
382 child->stashExternalState(internalUnits, seenChildren);
394 for (
auto &[name, child] : m_children) {
400 if (!child->prefix.isEmpty()) {
401 child->m_object = m_object;
402 child->refreshObjects();
406 if (QObject *newObj = m_object->property(name.toUtf8()).value<QObject *>())
407 child->m_object = newObj;
409 child->refreshObjects();
419 for (
auto &stored : m_storedBindings) {
427 if (stored.sourceGuard.isNull()) {
429 if (
auto *abstractBinding = stored.binding.asAbstractBinding()) {
430 isPTP = abstractBinding->kind()
431 == QQmlAbstractBinding::PropertyToPropertyBinding;
432 }
else if (
const QPropertyBindingPrivate *priv = QPropertyBindingPrivate::get(
433 stored.binding.asUntypedPropertyBinding())) {
434 const auto base =
static_cast<
const QQmlPropertyBindingBase *>(priv);
435 isPTP = base->bindingKind()
436 == QQmlPropertyBindingBase::BindingKind::PropertyToProperty;
442 QQmlProperty qProp(m_object, stored.propertyName);
443 if (!qProp.isValid())
450 if (
auto *abstractBinding = stored.binding.asAbstractBinding()) {
451 if (abstractBinding->targetObject() != qProp.object())
452 abstractBinding->setTarget(qProp);
455 stored.binding.installOn(qProp);
457 m_storedBindings.clear();
460 for (
auto &stored : m_storedValues) {
461 const QMetaObject *mo = m_object->metaObject();
462 const int idx = mo->indexOfProperty(stored.propertyName.toUtf8().constData());
467 QQmlProperty qProp(m_object, stored.propertyName);
468 if (!qProp.isValid())
470 QQmlAnyBinding currentBinding = QQmlAnyBinding::ofProperty(qProp);
474 mo->property(idx).write(m_object, stored.value);
476 m_storedValues.clear();
480 if (!m_storedSignalHandlers.empty()) {
481 QQmlEngine *engine = unit->engine->qmlEngine();
482 for (
auto &stored : m_storedSignalHandlers) {
483 const QMetaObject *metaObject = m_object->metaObject();
484 const int signalIndex = QMetaObjectPrivate::signalIndex(
485 metaObject->method(metaObject->indexOfSignal(stored.signature.toUtf8())));
486 if (signalIndex >= 0)
487 QQmlData::connectEndpoint(stored.handler.release(), m_object, signalIndex, engine);
490 m_storedSignalHandlers.clear();
493 for (
auto &[name, child] : m_children) {
495 child->restoreExternalState();
501 const QV4::CompiledData::Binding *binding,
502 QDuplicateTracker<QObject *> *seenChildren)
504 const QString name = unit->stringAt(binding->propertyNameIndex);
506 const size_t size = m_children.size();
507 std::unique_ptr<BindingPatchContext> &child = m_children[name];
508 if (size == m_children.size())
514 if (QObject *groupObject = m_object->property(name.toUtf8()).value<QObject *>()) {
515 if (seenChildren->hasSeen(groupObject))
517 child = std::make_unique<BindingPatchContext>(groupObject, unit,
518 binding->value.objectIndex);
520 child = std::make_unique<BindingPatchContext>(m_object, unit, binding->value.objectIndex,
528 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
529 int objectIndex, QDuplicateTracker<QObject *> *seenChildren)
531 const size_t size = m_children.size();
532 std::unique_ptr<BindingPatchContext> &child = m_children[name];
533 if (size == m_children.size()) {
534 Q_ASSERT(!child || child->m_object == object);
538 if (!seenChildren || seenChildren->hasSeen(object))
541 child = std::make_unique<BindingPatchContext>(object, unit, objectIndex);
547 const QV4::CompiledData::Binding *binding,
548 QDuplicateTracker<QObject *> *seenChildren)
550 const QString name = unit->stringAt(binding->propertyNameIndex);
552 const size_t size = m_children.size();
553 std::unique_ptr<BindingPatchContext> &child = m_children[name];
554 if (size == m_children.size())
560 QV4::ResolvedTypeReference *typeRef = unit->resolvedType(binding->propertyNameIndex);
562 QQmlAttachedPropertiesFunc func =
563 typeRef->type().attachedPropertiesFunction(unit->engine->typeLoader());
566 if (QObject *attached = QQmlData::get(m_object)->attachedProperties()->value(func)) {
567 if (seenChildren->hasSeen(attached))
569 child = std::make_unique<BindingPatchContext>(attached, unit, binding->value.objectIndex);
575 const std::vector<QQmlRefPointer<QV4::ExecutableCompilationUnit>> &unitsToUnparent,
576 const std::vector<CompositeLevel> &internalUnits)
578 QQmlData *ddata = QQmlData::get(m_object);
580 const auto levelFor = [&internalUnits](
581 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldCu,
int oldIndex) {
582 for (
const CompositeLevel &level : internalUnits) {
583 if (level.oldCu == oldCu && level.objectIndex == oldIndex)
589 const QHash<QQmlAttachedPropertiesFunc, QObject *> *attachedProperties =
590 ddata->hasExtendedData() ? ddata->attachedProperties() :
nullptr;
591 QObjectList children = m_object->children();
595 const auto newEnd = std::remove_if(children.begin(), children.end(), [&](QObject *child) {
596 const QQmlData *childDdata = QQmlData::get(child);
600 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &cu = childDdata->compilationUnit;
604 if (std::find(unitsToUnparent.begin(), unitsToUnparent.end(), cu) == unitsToUnparent.end())
607 if (!attachedProperties)
610 for (QObject *attached : *attachedProperties) {
611 if (attached == child)
617 children.erase(newEnd, children.end());
624 for (QObject *child : children)
625 clearBindingsRecursive(child);
628 resetBindings(unit, objectIndex, level.newCu, level.objectIndex);
630 for (QQmlVMEMetaObject *vmeMeta = ddata->hasVMEMetaObject
631 ?
static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(m_object)->metaObject)
633 vmeMeta; vmeMeta = vmeMeta->parentVMEMetaObject()) {
634 const CompositeLevel level = levelFor(vmeMeta->compilationUnit(), vmeMeta->qmlObjectId());
635 resetBindings(vmeMeta->compilationUnit(), vmeMeta->qmlObjectId(), level.newCu,
642 while (QQmlBoundSignal *signalHandler = ddata->signalHandlers)
643 delete signalHandler;
648 for (QObject *child : children)
661 const QMetaObject *mo = object->metaObject();
662 if (
const int classInfoIndex = mo->indexOfClassInfo(
"ParentProperty"); classInfoIndex >= 0) {
663 const QMetaClassInfo classInfo = mo->classInfo(classInfoIndex);
664 if (
const int propertyIndex = mo->indexOfProperty(classInfo.value()); propertyIndex >= 0) {
665 const QMetaProperty property = mo->property(propertyIndex);
666 if ((!property.isResettable() || !property.reset(object)) && property.isWritable())
667 property.write(object, QVariant(property.metaType()));
674 QQml_setParent_noEvent(object,
nullptr);
675 object->deleteLater();
680 QQueue<QObject *> queue;
681 queue.enqueue(object);
683 while (!queue.isEmpty()) {
684 QObject *next = queue.dequeue();
685 queue.append(next->children());
687 QQmlData *ddata = QQmlData::get(next);
691 while (ddata->bindings)
692 QQmlPropertyPrivate::removeBinding(ddata->bindings);
697 const QMetaObject *mo = next->metaObject();
698 for (
int i = 0, n = mo->propertyCount(); i < n; ++i) {
699 if (!ddata->hasBindingBit(i))
701 const QMetaProperty prop = mo->property(i);
702 if (!prop.isBindable())
704 QUntypedBindable bindable = prop.bindable(next);
705 if (bindable.hasBinding())
706 bindable.takeBinding();
712 for (QQmlBoundSignal *sig = ddata->signalHandlers; sig; sig = sig->m_nextSignal)
713 sig->setEnabled(
false);
721 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &unit,
int cuIndex)
723 ReboundBindings bindings;
724 if (!unit || cuIndex < 0 || cuIndex >= unit->objectCount())
727 const QV4::CompiledData::Object *obj = unit->objectAt(cuIndex);
728 const QQmlPropertyCache::ConstPtr cache = unit->propertyCachesPtr()->at(cuIndex);
729 const QString defaultPropertyName = cache ? cache->defaultPropertyName() : QString();
731 for (
auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) {
732 const QString name = binding->propertyNameIndex == 0
733 ? defaultPropertyName
734 : unit->stringAt(binding->propertyNameIndex);
735 bindings.insert(name, binding);
743 const QString &name, QV4::CompiledData::Binding::Type type)
745 const QV4::CompiledData::Binding *newBinding = rebound.value(name);
746 return (newBinding && newBinding->type() == type) ? newBinding->value.objectIndex : -1;
750 const QV4::CompiledData::Binding *binding,
const QString &name,
751 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
752 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit,
753 const ReboundBindings &rebound)
755 if (binding->hasFlag(QV4::CompiledData::Binding::IsCustomParserBinding))
758 QQmlProperty prop(m_object, prefix + name);
759 QQmlPropertyIndex propIdx = QQmlPropertyPrivate::propertyIndex(prop);
760 Q_ASSERT(propIdx.coreIndex() >= 0);
762 const QMetaType type = prop.propertyMetaType();
764 const QMetaType::TypeFlags flags = type.flags();
765 if (flags.testFlag(QMetaType::IsQmlList)) {
768 QQmlListReference list = prop.read().value<QQmlListReference>();
771 }
else if (flags.testFlag(QMetaType::PointerToQObject) && binding->isGroupProperty()) {
773 child->resetBindings(
774 oldUnit, binding->value.objectIndex, newUnit,
775 reboundSubObjectIndex(rebound, name,
776 QV4::CompiledData::Binding::Type_GroupProperty));
782 if (rebound.contains(name))
785 if ((!prop.isResettable() || !prop.reset()) && prop.isWritable())
786 prop.write(QVariant(type));
790 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &oldUnit,
int cuIndex,
791 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &newUnit,
int newCuIndex)
793 const QV4::CompiledData::Object *obj = oldUnit->objectAt(cuIndex);
794 const QQmlPropertyCache::ConstPtr cache = oldUnit->propertyCachesPtr()->at(cuIndex);
795 const QString defaultPropertyName = cache ? cache->defaultPropertyName() : QString();
797 const ReboundBindings rebound = reboundBindings(newUnit, newCuIndex);
799 for (
auto binding = obj->bindingsBegin(), end = obj->bindingsEnd(); binding != end; ++binding) {
800 const QString name = binding->propertyNameIndex == 0
801 ? defaultPropertyName
802 : oldUnit->stringAt(binding->propertyNameIndex);
806 if (binding->isAttachedProperty()) {
809 if (!QQmlData::get(m_object)->hasExtendedData())
813 attached->resetBindings(
814 oldUnit, binding->value.objectIndex, newUnit,
815 reboundSubObjectIndex(rebound, name,
816 QV4::CompiledData::Binding::Type_AttachedProperty));
823 if (!binding->isSignalHandler())
824 resetBinding(binding, name, oldUnit, newUnit, rebound);
void restoreExternalState()
void reset(const std::vector< QQmlRefPointer< QV4::ExecutableCompilationUnit > > &unitsToUnparent, const std::vector< CompositeLevel > &internalUnits)
BindingPatchContext * attachedContext(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &unit, const QV4::CompiledData::Binding *binding, QDuplicateTracker< QObject * > *seenChildren)
BindingPatchContext * childContext(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &unit, const QV4::CompiledData::Binding *binding, QDuplicateTracker< QObject * > *seenChildren)
BindingPatchContext * childContext(const QString &name, QObject *object, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &unit, int objectIndex, QDuplicateTracker< QObject * > *seenChildren)
void stashExternalState(const std::vector< CompositeLevel > &internalUnits, QDuplicateTracker< QObject * > *seenChildren)
static ReboundBindings reboundBindings(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &unit, int cuIndex)
static bool functionBelongsToObject(const QV4::Function *f, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &cu, int objectIndex)
static QObject * propertyToPropertySource(const QQmlAnyBinding &binding)
static int reboundSubObjectIndex(const ReboundBindings &rebound, const QString &name, QV4::CompiledData::Binding::Type type)
static bool isExternalBinding(const QQmlAnyBinding &binding, const std::vector< CompositeLevel > &internalUnits, QObject *target)
Combined button and popup list for selecting options.