From f75bd7eb78759a2b708771517b5fc64fd7a75e8a Mon Sep 17 00:00:00 2001 From: Martin Jones Date: Tue, 1 Nov 2011 10:05:47 +1000 Subject: [PATCH] Ensure that chained incubation works from componentCompleted. Make chained AsynchronousIfNested initiated from componentComplete work correctly, i.e. asynchronous incubator is not Ready until all chained creation is Ready. Change-Id: I286cc10e2f09e36dcc6034f3f23681e833d7e6e8 Reviewed-by: Aaron Kennedy --- src/declarative/qml/qdeclarativecontext.cpp | 4 +- src/declarative/qml/qdeclarativecontext_p.h | 5 +- src/declarative/qml/qdeclarativeengine.cpp | 3 + src/declarative/qml/qdeclarativeincubator.cpp | 17 ++- src/declarative/qml/qdeclarativeincubator_p.h | 2 + src/declarative/qml/qdeclarativevme.cpp | 25 ++-- src/declarative/qml/qdeclarativevme_p.h | 2 +- .../data/chainInCompletion.qml | 5 + .../qdeclarativeincubator/testtypes.cpp | 28 +++++ .../declarative/qdeclarativeincubator/testtypes.h | 18 +++ .../tst_qdeclarativeincubator.cpp | 129 ++++++++++++++++++++ 11 files changed, 216 insertions(+), 22 deletions(-) create mode 100644 tests/auto/declarative/qdeclarativeincubator/data/chainInCompletion.qml diff --git a/src/declarative/qml/qdeclarativecontext.cpp b/src/declarative/qml/qdeclarativecontext.cpp index c9ae825..c4fc764 100644 --- a/src/declarative/qml/qdeclarativecontext.cpp +++ b/src/declarative/qml/qdeclarativecontext.cpp @@ -511,7 +511,7 @@ QObject *QDeclarativeContextPrivate::context_at(QDeclarativeListPropertyv8engine()->gc(); + + if (d->incubationController) + d->incubationController->d = 0; } /*! \fn void QDeclarativeEngine::quit() diff --git a/src/declarative/qml/qdeclarativeincubator.cpp b/src/declarative/qml/qdeclarativeincubator.cpp index 05c73da..80bdfac 100644 --- a/src/declarative/qml/qdeclarativeincubator.cpp +++ b/src/declarative/qml/qdeclarativeincubator.cpp @@ -65,8 +65,8 @@ void QDeclarativeEnginePrivate::incubate(QDeclarativeIncubator &i, QDeclarativeC QDeclarativeIncubatorPrivate *parentIncubator = 0; QDeclarativeContextData *cctxt = forContext; while (cctxt) { - if (cctxt->activeVME) { - parentIncubator = (QDeclarativeIncubatorPrivate *)cctxt->activeVME->data; + if (cctxt->activeVMEData) { + parentIncubator = (QDeclarativeIncubatorPrivate *)cctxt->activeVMEData; break; } cctxt = cctxt->parent; @@ -113,6 +113,8 @@ and it does not take ownership of it. void QDeclarativeEngine::setIncubationController(QDeclarativeIncubationController *controller) { Q_D(QDeclarativeEngine); + if (d->incubationController) + d->incubationController->d = 0; d->incubationController = controller; if (controller) controller->d = d; } @@ -155,6 +157,10 @@ void QDeclarativeIncubatorPrivate::clear() component->release(); component = 0; } + if (!rootContext.isNull()) { + rootContext->activeVMEData = 0; + rootContext = 0; + } if (nextWaitingFor.isInList()) { Q_ASSERT(waitingOnMe); @@ -250,6 +256,8 @@ void QDeclarativeIncubationController::incubatingObjectCountChanged(int incubati void QDeclarativeIncubatorPrivate::incubate(QDeclarativeVME::Interrupt &i) { + if (!component) + return; typedef QDeclarativeIncubatorPrivate IP; QRecursionWatcher watcher(this); @@ -311,7 +319,9 @@ void QDeclarativeIncubatorPrivate::incubate(QDeclarativeVME::Interrupt &i) if (watcher.hasRecursed()) return; - if (vme.complete(i)) { + QDeclarativeContextData *ctxt = vme.complete(i); + if (ctxt) { + rootContext = ctxt; progress = QDeclarativeIncubatorPrivate::Completed; goto finishIncubate; } @@ -570,7 +580,6 @@ void QDeclarativeIncubator::forceCompletion() if (Loading == status()) d->incubate(i); } - } /*! diff --git a/src/declarative/qml/qdeclarativeincubator_p.h b/src/declarative/qml/qdeclarativeincubator_p.h index eaa4ce5..ebabbae 100644 --- a/src/declarative/qml/qdeclarativeincubator_p.h +++ b/src/declarative/qml/qdeclarativeincubator_p.h @@ -46,6 +46,7 @@ #include #include #include +#include // // W A R N I N G @@ -83,6 +84,7 @@ public: Progress progress; QObject *result; + QDeclarativeGuardedContextData rootContext; QDeclarativeCompiledData *component; QDeclarativeVME vme; QDeclarativeVMEGuard vmeGuard; diff --git a/src/declarative/qml/qdeclarativevme.cpp b/src/declarative/qml/qdeclarativevme.cpp index b7abef4..ad8d80c 100644 --- a/src/declarative/qml/qdeclarativevme.cpp +++ b/src/declarative/qml/qdeclarativevme.cpp @@ -310,7 +310,7 @@ QObject *QDeclarativeVME::run(QList *errors, } if (states.count() == 1) { rootContext = CTXT; - rootContext->activeVME = this; + rootContext->activeVMEData = data; } if (states.count() == 1 && !creationContext.isNull()) { // A component that is logically created within another component instance shares the @@ -1207,7 +1207,7 @@ void QDeclarativeVME::reset() delete objects.at(0); if (!rootContext.isNull()) - rootContext->activeVME = 0; + rootContext->activeVMEData = 0; // Remove the QDeclarativeParserStatus and QDeclarativeAbstractBinding back pointers blank(parserStatus); @@ -1342,7 +1342,7 @@ void **QDeclarativeVME::instructionJumpTable() } #endif -bool QDeclarativeVME::complete(const Interrupt &interrupt) +QDeclarativeContextData *QDeclarativeVME::complete(const Interrupt &interrupt) { Q_ASSERT(engine || (bindValues.isEmpty() && @@ -1352,7 +1352,7 @@ bool QDeclarativeVME::complete(const Interrupt &interrupt) finalizeCallbacks.isEmpty())); if (!engine) - return true; + return 0; ActiveVMERestorer restore(this, QDeclarativeEnginePrivate::get(engine)); QRecursionWatcher watcher(this); @@ -1367,7 +1367,7 @@ bool QDeclarativeVME::complete(const Interrupt &interrupt) } if (watcher.hasRecursed() || interrupt.shouldInterrupt()) - return false; + return 0; } bindValues.deallocate(); @@ -1380,7 +1380,7 @@ bool QDeclarativeVME::complete(const Interrupt &interrupt) } if (watcher.hasRecursed() || interrupt.shouldInterrupt()) - return false; + return 0; } parserStatus.deallocate(); @@ -1394,12 +1394,9 @@ bool QDeclarativeVME::complete(const Interrupt &interrupt) emit a->completed(); if (watcher.hasRecursed() || interrupt.shouldInterrupt()) - return false; + return 0; } - if (!rootContext.isNull()) - rootContext->activeVME = 0; - for (int ii = 0; ii < finalizeCallbacks.count(); ++ii) { QDeclarativeEnginePrivate::FinalizeCallback callback = finalizeCallbacks.at(ii); QObject *obj = callback.first; @@ -1408,13 +1405,17 @@ bool QDeclarativeVME::complete(const Interrupt &interrupt) QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, callback.second, args); } if (watcher.hasRecursed()) - return false; + return 0; } finalizeCallbacks.clear(); + QDeclarativeContextData *rv = rootContext; + reset(); - return true; + if (rv) rv->activeVMEData = data; + + return rv; } void QDeclarativeVME::blank(QFiniteStack &bs) diff --git a/src/declarative/qml/qdeclarativevme_p.h b/src/declarative/qml/qdeclarativevme_p.h index b74bc54..4edceb2 100644 --- a/src/declarative/qml/qdeclarativevme_p.h +++ b/src/declarative/qml/qdeclarativevme_p.h @@ -125,7 +125,7 @@ public: void reset(); QObject *execute(QList *errors, const Interrupt & = Interrupt()); - bool complete(const Interrupt & = Interrupt()); + QDeclarativeContextData *complete(const Interrupt & = Interrupt()); private: friend class QDeclarativeVMEGuard; diff --git a/tests/auto/declarative/qdeclarativeincubator/data/chainInCompletion.qml b/tests/auto/declarative/qdeclarativeincubator/data/chainInCompletion.qml new file mode 100644 index 0000000..e79fed3 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeincubator/data/chainInCompletion.qml @@ -0,0 +1,5 @@ +import Qt.test 1.0 + +SelfRegistering { + property variant a: CompletionCallback {} +} diff --git a/tests/auto/declarative/qdeclarativeincubator/testtypes.cpp b/tests/auto/declarative/qdeclarativeincubator/testtypes.cpp index 99d2cb1..6d6fb38 100644 --- a/tests/auto/declarative/qdeclarativeincubator/testtypes.cpp +++ b/tests/auto/declarative/qdeclarativeincubator/testtypes.cpp @@ -101,9 +101,37 @@ void CallbackRegisteringType::registerCallback(callback c, void *d) m_data = d; } +CompletionCallbackType::callback CompletionCallbackType::m_callback = 0; +void *CompletionCallbackType::m_data = 0; +CompletionCallbackType::CompletionCallbackType() +{ +} + +void CompletionCallbackType::classBegin() +{ +} + +void CompletionCallbackType::componentComplete() +{ + if (m_callback) m_callback(this, m_data); +} + +void CompletionCallbackType::clearCallback() +{ + m_callback = 0; + m_data = 0; +} + +void CompletionCallbackType::registerCallback(callback c, void *d) +{ + m_callback = c; + m_data = d; +} + void registerTypes() { qmlRegisterType("Qt.test", 1,0, "SelfRegistering"); qmlRegisterType("Qt.test", 1,0, "CompletionRegistering"); qmlRegisterType("Qt.test", 1,0, "CallbackRegistering"); + qmlRegisterType("Qt.test", 1,0, "CompletionCallback"); } diff --git a/tests/auto/declarative/qdeclarativeincubator/testtypes.h b/tests/auto/declarative/qdeclarativeincubator/testtypes.h index 6e73254..8d9968d 100644 --- a/tests/auto/declarative/qdeclarativeincubator/testtypes.h +++ b/tests/auto/declarative/qdeclarativeincubator/testtypes.h @@ -100,6 +100,24 @@ private: static CompletionRegisteringType *m_me; }; +class CompletionCallbackType : public QObject, public QDeclarativeParserStatus +{ +Q_OBJECT +public: + CompletionCallbackType(); + + virtual void classBegin(); + virtual void componentComplete(); + + typedef void (*callback)(CompletionCallbackType *, void *); + static void clearCallback(); + static void registerCallback(callback, void *); + +private: + static callback m_callback; + static void *m_data; +}; + void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/declarative/qdeclarativeincubator/tst_qdeclarativeincubator.cpp b/tests/auto/declarative/qdeclarativeincubator/tst_qdeclarativeincubator.cpp index 684cd35..54ca622 100644 --- a/tests/auto/declarative/qdeclarativeincubator/tst_qdeclarativeincubator.cpp +++ b/tests/auto/declarative/qdeclarativeincubator/tst_qdeclarativeincubator.cpp @@ -83,6 +83,7 @@ private slots: void asynchronousIfNested(); void nestedComponent(); void chainedAsynchronousIfNested(); + void chainedAsynchronousIfNestedOnCompleted(); void selfDelete(); private: @@ -773,6 +774,134 @@ void tst_qdeclarativeincubator::chainedAsynchronousIfNested() QVERIFY(incubator2.isReady()); } +// Checks that new AsynchronousIfNested incubators can be correctly chained if started in +// componentCompleted(). +void tst_qdeclarativeincubator::chainedAsynchronousIfNestedOnCompleted() +{ + SelfRegisteringType::clearMe(); + + QDeclarativeComponent component(&engine, TEST_FILE("chainInCompletion.qml")); + QVERIFY(component.isReady()); + + QDeclarativeComponent c1(&engine, TEST_FILE("chainedAsynchronousIfNested.qml")); + QVERIFY(c1.isReady()); + + struct MyIncubator : public QDeclarativeIncubator { + MyIncubator(MyIncubator *next, QDeclarativeComponent *component, QDeclarativeContext *ctxt) + : QDeclarativeIncubator(AsynchronousIfNested), next(next), component(component), ctxt(ctxt) {} + + protected: + virtual void statusChanged(Status s) { + if (s == Ready && next) { + component->create(*next, 0, ctxt); + } + } + + private: + MyIncubator *next; + QDeclarativeComponent *component; + QDeclarativeContext *ctxt; + }; + + struct CallbackData { + CallbackData(QDeclarativeComponent *c, MyIncubator *i, QDeclarativeContext *ct) + : component(c), incubator(i), ctxt(ct) {} + QDeclarativeComponent *component; + MyIncubator *incubator; + QDeclarativeContext *ctxt; + static void callback(CompletionCallbackType *o, void *data) { + CallbackData *d = (CallbackData *)data; + d->component->create(*d->incubator, 0, d->ctxt); + } + }; + + QDeclarativeIncubator incubator(QDeclarativeIncubator::Asynchronous); + component.create(incubator); + + QVERIFY(incubator.isLoading()); + QVERIFY(SelfRegisteringType::me() == 0); + + while (SelfRegisteringType::me() == 0 && incubator.isLoading()) { + bool b = false; + controller.incubateWhile(&b); + } + + QVERIFY(SelfRegisteringType::me() != 0); + QVERIFY(incubator.isLoading()); + + MyIncubator incubator3(0, &c1, qmlContext(SelfRegisteringType::me())); + MyIncubator incubator2(&incubator3, &c1, qmlContext(SelfRegisteringType::me())); + MyIncubator incubator1(&incubator2, &c1, qmlContext(SelfRegisteringType::me())); + + // start incubator1 in componentComplete + CallbackData cd(&c1, &incubator1, qmlContext(SelfRegisteringType::me())); + CompletionCallbackType::registerCallback(&CallbackData::callback, &cd); + + while (!incubator1.isLoading()) { + QVERIFY(incubator.isLoading()); + QVERIFY(incubator2.isNull()); + QVERIFY(incubator3.isNull()); + + bool b = false; + controller.incubateWhile(&b); + } + + QVERIFY(incubator.isLoading()); + QVERIFY(incubator1.isLoading()); + QVERIFY(incubator2.isNull()); + QVERIFY(incubator3.isNull()); + + while (incubator1.isLoading()) { + QVERIFY(incubator.isLoading()); + QVERIFY(incubator1.isLoading()); + QVERIFY(incubator2.isNull()); + QVERIFY(incubator3.isNull()); + + bool b = false; + controller.incubateWhile(&b); + } + + QVERIFY(incubator.isLoading()); + QVERIFY(incubator1.isReady()); + QVERIFY(incubator2.isLoading()); + QVERIFY(incubator3.isNull()); + + while (incubator2.isLoading()) { + QVERIFY(incubator.isLoading()); + QVERIFY(incubator1.isReady()); + QVERIFY(incubator2.isLoading()); + QVERIFY(incubator3.isNull()); + + bool b = false; + controller.incubateWhile(&b); + } + + QVERIFY(incubator.isLoading()); + QVERIFY(incubator1.isReady()); + QVERIFY(incubator2.isReady()); + QVERIFY(incubator3.isLoading()); + + while (incubator3.isLoading()) { + QVERIFY(incubator.isLoading()); + QVERIFY(incubator1.isReady()); + QVERIFY(incubator2.isReady()); + QVERIFY(incubator3.isLoading()); + + bool b = false; + controller.incubateWhile(&b); + } + + { + bool b = true; + controller.incubateWhile(&b); + } + + QVERIFY(incubator.isReady()); + QVERIFY(incubator1.isReady()); + QVERIFY(incubator2.isReady()); + QVERIFY(incubator3.isReady()); +} + void tst_qdeclarativeincubator::selfDelete() { struct MyIncubator : public QDeclarativeIncubator { -- 1.7.2.5