Skip to content

Commit

Permalink
Fix issue with bindings to aliases that cannot yet be resolved
Browse files Browse the repository at this point in the history
When an alias points to a child object which has not yet been
initialized, it's id won't have been registered yet, so setting up a
binding to it will result in a crash.

The fix is: when setting a binding target fails, and its target property
is an alias, queue them until all bindings have been set up, and try
again.

Task-number: QTBUG-57041
Change-Id: I4dc5a6d25c0a32fed9fd952c955e2006c76be45a
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
(cherry picked from commit aa94c6c0469b0595f483f13ac88459f0035deef9)
(cherry picked from commit c3db3cfa296dbc5aa198520c1411830d165cd496)
  • Loading branch information
Erik Verbruggen authored and jaheikk committed Mar 20, 2018
1 parent dc0136b commit 52b1993
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 60 deletions.
13 changes: 8 additions & 5 deletions src/qml/qml/qqmlbinding.cpp
Expand Up @@ -465,13 +465,13 @@ void QQmlBinding::setTarget(const QQmlProperty &prop)
setTarget(prop.object(), pd->core, &pd->valueTypeData);
}

void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType)
bool QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType)
{
m_target = object;

if (!object) {
m_targetIndex = QQmlPropertyIndex();
return;
return false;
}

int coreIndex = core.coreIndex();
Expand All @@ -481,9 +481,10 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const

int aValueTypeIndex;
if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) {
m_target = 0;
// can't resolve id (yet)
m_target = nullptr;
m_targetIndex = QQmlPropertyIndex();
return;
return false;
}
if (valueTypeIndex == -1)
valueTypeIndex = aValueTypeIndex;
Expand All @@ -492,7 +493,7 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const
if (!data || !data->propertyCache) {
m_target = 0;
m_targetIndex = QQmlPropertyIndex();
return;
return false;
}
QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex);
Q_ASSERT(propertyData);
Expand All @@ -508,6 +509,8 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const
data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject());
data->propertyCache->addref();
}

return true;
}

void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const
Expand Down
4 changes: 3 additions & 1 deletion src/qml/qml/qqmlbinding_p.h
Expand Up @@ -72,6 +72,8 @@ class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlJavaScriptExpression,
{
friend class QQmlAbstractBinding;
public:
typedef QExplicitlySharedDataPointer<QQmlBinding> Ptr;

static QQmlBinding *create(const QQmlPropertyData *, const QQmlScriptString &, QObject *, QQmlContext *);
static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContextData *,
const QString &url = QString(), quint16 lineNumber = 0);
Expand All @@ -80,7 +82,7 @@ class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlJavaScriptExpression,
~QQmlBinding();

void setTarget(const QQmlProperty &);
void setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType);
bool setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType);

void setNotifyOnValueChanged(bool);

Expand Down
139 changes: 85 additions & 54 deletions src/qml/qml/qqmlobjectcreator.cpp

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/qml/qml/qqmlobjectcreator_p.h
Expand Up @@ -161,6 +161,9 @@ class Q_QML_PRIVATE_EXPORT QQmlObjectCreator
QV4::QmlContext *_qmlContext;

friend struct QQmlObjectCreatorRecursionWatcher;

typedef std::function<bool(QQmlObjectCreatorSharedState *sharedState)> PendingAliasBinding;
std::vector<PendingAliasBinding> pendingAliasBindings;
};

struct QQmlObjectCreatorRecursionWatcher
Expand Down
1 change: 1 addition & 0 deletions src/qml/qml/qqmlproperty.cpp
Expand Up @@ -875,6 +875,7 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, QQmlPropertyIndex bin
void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, QQmlPropertyData::WriteFlags writeFlags)
{
Q_ASSERT(binding);
Q_ASSERT(binding->targetObject());

QObject *object = binding->targetObject();
const QQmlPropertyIndex index = binding->targetPropertyIndex();
Expand Down
15 changes: 15 additions & 0 deletions tests/auto/qml/qqmllanguage/data/alias.16.qml
@@ -0,0 +1,15 @@
import QtQuick 2.0
import QtQuick.Window 2.0

Window {
visible: true

property alias list: repeater.model

list: ["A", "B", "C"]

Repeater {
id: repeater
}
}

10 changes: 10 additions & 0 deletions tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
Expand Up @@ -1872,6 +1872,16 @@ void tst_qqmllanguage::aliasProperties()

QVERIFY(subObject->property("success").toBool());
}

// Alias to sub-object with binding (QTBUG-57041)
{
// This is shold *not* crash.
QQmlComponent component(&engine, testFileUrl("alias.16.qml"));
VERIFY_ERRORS(0);

QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
}
}

// QTBUG-13374 Test that alias properties and signals can coexist
Expand Down

0 comments on commit 52b1993

Please sign in to comment.