diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2018-03-19 10:18:57 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2018-03-21 08:45:28 +0000 |
commit | aceeec0a85cc50fec70e3142936332a3b09e7061 (patch) | |
tree | 059990158695a886d72bf18e25b5b30c489e2103 | |
parent | ce5b33c350cb7aa4a649088d85e48be08b632b69 (diff) |
Fix crash with StackView::initialItem
The following binding
initialView: Qt.createComponent("blah.qml")
works with QQC1 but crashes with QQC2 as soon as the garbage collector
kicks in. In QQC1 StackView was implemented in a .qml file and the
property was declared as
property var initialItem
For such declared properties the QML engine takes care of ensuring that
the reference assigned is a strong reference towards the GC and it also
tracks the life-time of the QObject in case it's explicitly deleted by
somebody else.
In QQC2 the property continues to be documented as "var" property,
however it is implemented in C++ as QVariant property. The QVariant is
not known to the GC at all and it also doesn't do life-cycle tracking.
The C++ equivalent to "var" in QML is QJSValue, which is a strong
reference and also ends up using a QPointer internally when holding a
QObject.
Task-number: QTBUG-67118
Change-Id: I43e473c7bf2c40f9843e9d50fe4fd805b54fd256
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
-rw-r--r-- | src/quicktemplates2/qquickstackview.cpp | 8 | ||||
-rw-r--r-- | src/quicktemplates2/qquickstackview_p.h | 6 | ||||
-rw-r--r-- | src/quicktemplates2/qquickstackview_p_p.h | 2 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_stackview.qml | 15 |
4 files changed, 23 insertions, 8 deletions
diff --git a/src/quicktemplates2/qquickstackview.cpp b/src/quicktemplates2/qquickstackview.cpp index 1abc506b..6dae65c8 100644 --- a/src/quicktemplates2/qquickstackview.cpp +++ b/src/quicktemplates2/qquickstackview.cpp @@ -898,13 +898,13 @@ void QQuickStackView::clear(Operation operation) \sa push() */ -QVariant QQuickStackView::initialItem() const +QJSValue QQuickStackView::initialItem() const { Q_D(const QQuickStackView); return d->initialItem; } -void QQuickStackView::setInitialItem(const QVariant &item) +void QQuickStackView::setInitialItem(const QJSValue &item) { Q_D(QQuickStackView); d->initialItem = item; @@ -1081,9 +1081,9 @@ void QQuickStackView::componentComplete() QQuickStackElement *element = nullptr; QString error; int oldDepth = d->elements.count(); - if (QObject *o = d->initialItem.value<QObject *>()) + if (QObject *o = d->initialItem.toQObject()) element = QQuickStackElement::fromObject(o, this, &error); - else if (d->initialItem.canConvert<QString>()) + else if (d->initialItem.isString()) element = QQuickStackElement::fromString(d->initialItem.toString(), this, &error); if (!error.isEmpty()) { d->warn(error); diff --git a/src/quicktemplates2/qquickstackview_p.h b/src/quicktemplates2/qquickstackview_p.h index e347ba9d..ba6fe106 100644 --- a/src/quicktemplates2/qquickstackview_p.h +++ b/src/quicktemplates2/qquickstackview_p.h @@ -65,7 +65,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickStackView : public QQuickControl Q_PROPERTY(bool busy READ isBusy NOTIFY busyChanged FINAL) Q_PROPERTY(int depth READ depth NOTIFY depthChanged FINAL) Q_PROPERTY(QQuickItem *currentItem READ currentItem NOTIFY currentItemChanged FINAL) - Q_PROPERTY(QVariant initialItem READ initialItem WRITE setInitialItem FINAL) + Q_PROPERTY(QJSValue initialItem READ initialItem WRITE setInitialItem FINAL) Q_PROPERTY(QQuickTransition *popEnter READ popEnter WRITE setPopEnter NOTIFY popEnterChanged FINAL) Q_PROPERTY(QQuickTransition *popExit READ popExit WRITE setPopExit NOTIFY popExitChanged FINAL) Q_PROPERTY(QQuickTransition *pushEnter READ pushEnter WRITE setPushEnter NOTIFY pushEnterChanged FINAL) @@ -93,8 +93,8 @@ public: }; Q_ENUM(Status) - QVariant initialItem() const; - void setInitialItem(const QVariant &item); + QJSValue initialItem() const; + void setInitialItem(const QJSValue &item); QQuickTransition *popEnter() const; void setPopEnter(QQuickTransition *enter); diff --git a/src/quicktemplates2/qquickstackview_p_p.h b/src/quicktemplates2/qquickstackview_p_p.h index d86f35cd..7f1b77b1 100644 --- a/src/quicktemplates2/qquickstackview_p_p.h +++ b/src/quicktemplates2/qquickstackview_p_p.h @@ -96,7 +96,7 @@ public: bool busy; QString operation; - QVariant initialItem; + QJSValue initialItem; QQuickItem *currentItem; QSet<QQuickStackElement*> removing; QList<QQuickStackElement*> removed; diff --git a/tests/auto/controls/data/tst_stackview.qml b/tests/auto/controls/data/tst_stackview.qml index 3c0f0273..3827354b 100644 --- a/tests/auto/controls/data/tst_stackview.qml +++ b/tests/auto/controls/data/tst_stackview.qml @@ -1230,4 +1230,19 @@ TestCase { touch.release(0, control).commit() verify(!ma.pressed) } + + // Separate function to ensure that the temporary value created to hold the return value of the Qt.createComponent() + // call is out of scope when the caller calls gc(). + function stackViewFactory() + { + return createTemporaryObject(stackView, testCase, {initialItem: Qt.createComponent("TestItem.qml")}) + } + + function test_initalItemOwnership() + { + var control = stackViewFactory() + verify(control) + gc() + verify(control.initialItem) + } } |