aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ-P Nurmi <jpnurmi@qt.io>2017-09-14 13:24:42 +0200
committerJ-P Nurmi <jpnurmi@qt.io>2017-10-18 10:12:05 +0000
commit3b6eeee177b64eebe240d51be0c7bb5f031471d8 (patch)
treef8f579d881da44a2fd2c0f25f27587e541882061
parent5a03ab88c9bcff60744220c5f2dc91c5e1f363c8 (diff)
Fix execution of deferred properties
When deferred properties were assigned in multiple contexts, only the outermost context was executed. Any deferred property assignments in other inner contexts were never executed. Collect the deferred data to a container to be able to execute them all. Task-number: QTBUG-63200 Change-Id: I88fab27c1f81b5188430ada086dcc19842507e99 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
-rw-r--r--src/qml/qml/qqmlcomponent.cpp36
-rw-r--r--src/qml/qml/qqmlcomponent_p.h13
-rw-r--r--src/qml/qml/qqmldata_p.h5
-rw-r--r--src/qml/qml/qqmlengine.cpp27
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp8
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h2
-rw-r--r--tests/auto/qml/qqmllanguage/data/MyDeferredProperties.qml19
-rw-r--r--tests/auto/qml/qqmllanguage/data/deferredProperties.qml19
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.cpp1
-rw-r--r--tests/auto/qml/qqmllanguage/testtypes.h15
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp48
11 files changed, 162 insertions, 31 deletions
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index ce35846c88..7f1121c1e1 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -878,19 +878,33 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context)
}
void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv,
- QObject *object, ConstructionState *state)
+ QObject *object, DeferredState *deferredState)
{
- enginePriv->inProgressCreations++;
- state->errors.clear();
- state->completePending = true;
-
QQmlData *ddata = QQmlData::get(object);
- Q_ASSERT(ddata->deferredData);
- QQmlData::DeferredData *deferredData = ddata->deferredData;
- QQmlContextData *creationContext = 0;
- state->creator.reset(new QQmlObjectCreator(deferredData->context->parent, deferredData->compilationUnit, creationContext));
- if (!state->creator->populateDeferredProperties(object))
- state->errors << state->creator->errors;
+ Q_ASSERT(!ddata->deferredData.isEmpty());
+
+ deferredState->constructionStates.reserve(ddata->deferredData.size());
+
+ for (QQmlData::DeferredData *deferredData : qAsConst(ddata->deferredData)) {
+ enginePriv->inProgressCreations++;
+
+ ConstructionState *state = new ConstructionState;
+ state->completePending = true;
+
+ QQmlContextData *creationContext = nullptr;
+ state->creator.reset(new QQmlObjectCreator(deferredData->context->parent, deferredData->compilationUnit, creationContext));
+
+ if (!state->creator->populateDeferredProperties(object, deferredData))
+ state->errors << state->creator->errors;
+
+ deferredState->constructionStates += state;
+ }
+}
+
+void QQmlComponentPrivate::completeDeferred(QQmlEnginePrivate *enginePriv, QQmlComponentPrivate::DeferredState *deferredState)
+{
+ for (ConstructionState *state : qAsConst(deferredState->constructionStates))
+ complete(enginePriv, state);
}
void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state)
diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h
index d01a987acc..2a57f7b247 100644
--- a/src/qml/qml/qqmlcomponent_p.h
+++ b/src/qml/qml/qqmlcomponent_p.h
@@ -114,8 +114,17 @@ public:
};
ConstructionState state;
- static void beginDeferred(QQmlEnginePrivate *enginePriv, QObject *object,
- ConstructionState *state);
+ struct DeferredState {
+ ~DeferredState() {
+ qDeleteAll(constructionStates);
+ constructionStates.clear();
+ }
+ QVector<ConstructionState *> constructionStates;
+ };
+
+ static void beginDeferred(QQmlEnginePrivate *enginePriv, QObject *object, DeferredState* deferredState);
+ static void completeDeferred(QQmlEnginePrivate *enginePriv, DeferredState *deferredState);
+
static void complete(QQmlEnginePrivate *enginePriv, ConstructionState *state);
QQmlEngine *engine;
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h
index 75ea720358..d692feb975 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.h
@@ -57,6 +57,7 @@
#include <private/qv4value_p.h>
#include <private/qv4persistent_p.h>
#include <qjsengine.h>
+#include <qvector.h>
QT_BEGIN_NAMESPACE
@@ -219,7 +220,9 @@ public:
QQmlContextData *context;//Could be either context or outerContext
};
QV4::CompiledData::CompilationUnit *compilationUnit;
- DeferredData *deferredData;
+ QVector<DeferredData *> deferredData;
+
+ void releaseDeferredData();
QV4::WeakValue jsWrapper;
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 13b17e6946..5a52224208 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -739,7 +739,7 @@ QQmlData::QQmlData()
hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false),
bindingBitsSize(MaxInlineBits), bindingBitsValue(0), notifyList(0),
bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0),
- lineNumber(0), columnNumber(0), jsEngineId(0), compilationUnit(0), deferredData(0),
+ lineNumber(0), columnNumber(0), jsEngineId(0), compilationUnit(0),
propertyCache(0), guards(0), extendedData(0)
{
init();
@@ -1469,18 +1469,16 @@ void qmlExecuteDeferred(QObject *object)
{
QQmlData *data = QQmlData::get(object);
- if (data && data->deferredData && !data->wasDeleted(object)) {
+ if (data && !data->deferredData.isEmpty() && !data->wasDeleted(object)) {
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine);
- QQmlComponentPrivate::ConstructionState state;
+ QQmlComponentPrivate::DeferredState state;
QQmlComponentPrivate::beginDeferred(ep, object, &state);
// Release the reference for the deferral action (we still have one from construction)
- data->deferredData->compilationUnit->release();
- delete data->deferredData;
- data->deferredData = 0;
+ data->releaseDeferredData();
- QQmlComponentPrivate::complete(ep, &state);
+ QQmlComponentPrivate::completeDeferred(ep, &state);
}
}
@@ -1638,6 +1636,15 @@ void QQmlData::NotifyList::layout()
todo = 0;
}
+void QQmlData::releaseDeferredData()
+{
+ for (DeferredData *deferData : qAsConst(deferredData)) {
+ deferData->compilationUnit->release();
+ delete deferData;
+ }
+ deferredData.clear();
+}
+
void QQmlData::addNotify(int index, QQmlNotifierEndpoint *endpoint)
{
if (!notifyList) {
@@ -1712,11 +1719,7 @@ void QQmlData::destroyed(QObject *object)
compilationUnit = 0;
}
- if (deferredData) {
- deferredData->compilationUnit->release();
- delete deferredData;
- deferredData = 0;
- }
+ releaseDeferredData();
QQmlBoundSignal *signalHandler = signalHandlers;
while (signalHandler) {
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 07a90d4d63..b2f1421bcb 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -233,10 +233,10 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
return instance;
}
-bool QQmlObjectCreator::populateDeferredProperties(QObject *instance)
+bool QQmlObjectCreator::populateDeferredProperties(QObject *instance, QQmlData::DeferredData *deferredData)
{
QQmlData *declarativeData = QQmlData::get(instance);
- context = declarativeData->deferredData->context;
+ context = deferredData->context;
sharedState->rootContext = context;
QObject *bindingTarget = instance;
@@ -260,7 +260,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance)
qSwap(_propertyCache, cache);
qSwap(_qobject, instance);
- int objectIndex = declarativeData->deferredData->deferredIdx;
+ int objectIndex = deferredData->deferredIdx;
qSwap(_compiledObjectIndex, objectIndex);
const QV4::CompiledData::Object *obj = qmlUnit->objectAt(_compiledObjectIndex);
@@ -1347,7 +1347,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
deferData->compilationUnit = compilationUnit;
deferData->compilationUnit->addref();
deferData->context = context;
- _ddata->deferredData = deferData;
+ _ddata->deferredData.append(deferData);
}
if (_compiledObject->nFunctions > 0)
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index a62825e19e..0c2d427c58 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -89,7 +89,7 @@ public:
~QQmlObjectCreator();
QObject *create(int subComponentIndex = -1, QObject *parent = 0, QQmlInstantiationInterrupt *interrupt = 0);
- bool populateDeferredProperties(QObject *instance);
+ bool populateDeferredProperties(QObject *instance, QQmlData::DeferredData *deferredData);
QQmlContextData *finalize(QQmlInstantiationInterrupt &interrupt);
void cancel(QObject *object);
void clear();
diff --git a/tests/auto/qml/qqmllanguage/data/MyDeferredProperties.qml b/tests/auto/qml/qqmllanguage/data/MyDeferredProperties.qml
new file mode 100644
index 0000000000..67e92e5a05
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/MyDeferredProperties.qml
@@ -0,0 +1,19 @@
+import QtQml 2.0
+import Test 1.0
+DeferredProperties {
+ groupProperty: QtObject {
+ objectName: "innerobj"
+ property bool wasCompleted: false
+ Component.onCompleted: wasCompleted = true
+ }
+ QtObject {
+ objectName: "innerlist1"
+ property bool wasCompleted: false
+ Component.onCompleted: wasCompleted = true
+ }
+ QtObject {
+ objectName: "innerlist2"
+ property bool wasCompleted: false
+ Component.onCompleted: wasCompleted = true
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/data/deferredProperties.qml b/tests/auto/qml/qqmllanguage/data/deferredProperties.qml
new file mode 100644
index 0000000000..07b146967c
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/deferredProperties.qml
@@ -0,0 +1,19 @@
+import QtQml 2.0
+import Test 1.0
+MyDeferredProperties {
+ groupProperty: QtObject {
+ objectName: "outerobj"
+ property bool wasCompleted: false
+ Component.onCompleted: wasCompleted = true
+ }
+ QtObject {
+ objectName: "outerlist1"
+ property bool wasCompleted: false
+ Component.onCompleted: wasCompleted = true
+ }
+ QtObject {
+ objectName: "outerlist2"
+ property bool wasCompleted: false
+ Component.onCompleted: wasCompleted = true
+ }
+}
diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp
index 72e06d26aa..d2240d25a9 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.cpp
+++ b/tests/auto/qml/qqmllanguage/testtypes.cpp
@@ -105,6 +105,7 @@ void registerTypes()
qmlRegisterType<MyArrayBufferTestClass>("Test", 1, 0, "MyArrayBufferTestClass");
qmlRegisterType<LazyDeferredSubObject>("Test", 1, 0, "LazyDeferredSubObject");
+ qmlRegisterType<DeferredProperties>("Test", 1, 0, "DeferredProperties");
}
QVariant myCustomVariantTypeConverter(const QString &data)
diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h
index b0e677feb8..d9ddff20a7 100644
--- a/tests/auto/qml/qqmllanguage/testtypes.h
+++ b/tests/auto/qml/qqmllanguage/testtypes.h
@@ -1351,6 +1351,21 @@ private:
QObject *obj;
};
+class DeferredProperties : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QObject *groupProperty MEMBER m_group)
+ Q_PROPERTY(QQmlListProperty<QObject> listProperty READ listProperty)
+ Q_CLASSINFO("DeferredPropertyNames", "groupProperty,listProperty")
+ Q_CLASSINFO("DefaultProperty", "listProperty")
+public:
+ QQmlListProperty<QObject> listProperty() { return QQmlListProperty<QObject>(this, m_list); }
+
+private:
+ QObject *m_group = 0;
+ QObjectList m_list;
+};
+
void registerTypes();
#endif // TESTTYPES_H
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index 30c34426af..f4d31d9e60 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -248,6 +248,7 @@ private slots:
void rootObjectInCreationNotForSubObjects();
void lazyDeferredSubObject();
+ void deferredProperties();
void noChildEvents();
@@ -4258,6 +4259,53 @@ void tst_qqmllanguage::lazyDeferredSubObject()
QCOMPARE(subObject->objectName(), QStringLiteral("custom"));
}
+// QTBUG-63200
+void tst_qqmllanguage::deferredProperties()
+{
+ QQmlComponent component(&engine, testFile("deferredProperties.qml"));
+ VERIFY_ERRORS(0);
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+
+ QObject *innerObj = object->findChild<QObject *>(QStringLiteral("innerobj"));
+ QVERIFY(!innerObj);
+
+ QObject *outerObj = object->findChild<QObject *>(QStringLiteral("outerobj"));
+ QVERIFY(!outerObj);
+
+ QObject *groupProperty = object->property("groupProperty").value<QObject *>();
+ QVERIFY(!groupProperty);
+
+ QQmlListProperty<QObject> listProperty = object->property("listProperty").value<QQmlListProperty<QObject>>();
+ QCOMPARE(listProperty.count(&listProperty), 0);
+
+ qmlExecuteDeferred(object.data());
+
+ innerObj = object->findChild<QObject *>(QStringLiteral("innerobj")); // MyDeferredListProperty.qml
+ QVERIFY(innerObj);
+ QCOMPARE(innerObj->property("wasCompleted"), QVariant(true));
+
+ outerObj = object->findChild<QObject *>(QStringLiteral("outerobj")); // deferredListProperty.qml
+ QVERIFY(outerObj);
+ QCOMPARE(outerObj->property("wasCompleted"), QVariant(true));
+
+ groupProperty = object->property("groupProperty").value<QObject *>();
+ QCOMPARE(groupProperty, outerObj);
+
+ listProperty = object->property("listProperty").value<QQmlListProperty<QObject>>();
+ QCOMPARE(listProperty.count(&listProperty), 4);
+
+ QCOMPARE(listProperty.at(&listProperty, 0)->objectName(), QStringLiteral("innerlist1")); // MyDeferredListProperty.qml
+ QCOMPARE(listProperty.at(&listProperty, 0)->property("wasCompleted"), QVariant(true));
+ QCOMPARE(listProperty.at(&listProperty, 1)->objectName(), QStringLiteral("innerlist2")); // MyDeferredListProperty.qml
+ QCOMPARE(listProperty.at(&listProperty, 1)->property("wasCompleted"), QVariant(true));
+
+ QCOMPARE(listProperty.at(&listProperty, 2)->objectName(), QStringLiteral("outerlist1")); // deferredListProperty.qml
+ QCOMPARE(listProperty.at(&listProperty, 2)->property("wasCompleted"), QVariant(true));
+ QCOMPARE(listProperty.at(&listProperty, 3)->objectName(), QStringLiteral("outerlist2")); // deferredListProperty.qml
+ QCOMPARE(listProperty.at(&listProperty, 3)->property("wasCompleted"), QVariant(true));
+}
+
void tst_qqmllanguage::noChildEvents()
{
QQmlComponent component(&engine);