diff options
author | Martin Jones <martin.jones@nokia.com> | 2011-11-01 10:05:47 +1000 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2011-11-01 13:09:31 +0100 |
commit | f75bd7eb78759a2b708771517b5fc64fd7a75e8a (patch) | |
tree | 3e71ed39482f9272afbc6feea2d6cf94b5555e48 | |
parent | c29b3c0974383dfe5e2b4890b5d6377d5aa4264e (diff) |
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 <aaron.kennedy@nokia.com>
11 files changed, 216 insertions, 22 deletions
diff --git a/src/declarative/qml/qdeclarativecontext.cpp b/src/declarative/qml/qdeclarativecontext.cpp index c9ae825ae2..c4fc764e7d 100644 --- a/src/declarative/qml/qdeclarativecontext.cpp +++ b/src/declarative/qml/qdeclarativecontext.cpp @@ -511,7 +511,7 @@ QObject *QDeclarativeContextPrivate::context_at(QDeclarativeListProperty<QObject QDeclarativeContextData::QDeclarativeContextData() : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false), - isPragmaLibraryContext(false), publicContext(0), activeVME(0), propertyNames(0), contextObject(0), + isPragmaLibraryContext(false), publicContext(0), activeVMEData(0), propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0), expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0), componentAttached(0), v4bindings(0), v8bindings(0) @@ -520,7 +520,7 @@ QDeclarativeContextData::QDeclarativeContextData() QDeclarativeContextData::QDeclarativeContextData(QDeclarativeContext *ctxt) : parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false), - isPragmaLibraryContext(false), publicContext(ctxt), activeVME(0), propertyNames(0), + isPragmaLibraryContext(false), publicContext(ctxt), activeVMEData(0), propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0), expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0), componentAttached(0), v4bindings(0), v8bindings(0) diff --git a/src/declarative/qml/qdeclarativecontext_p.h b/src/declarative/qml/qdeclarativecontext_p.h index ba4edb8e15..a6e83a0692 100644 --- a/src/declarative/qml/qdeclarativecontext_p.h +++ b/src/declarative/qml/qdeclarativecontext_p.h @@ -146,9 +146,8 @@ public: quint32 dummy:28; QDeclarativeContext *publicContext; - // VME that is constructing this context if any - // XXX remove if possible - QDeclarativeVME *activeVME; + // VME data that is constructing this context if any + void *activeVMEData; // Property name cache QDeclarativeIntegerCache *propertyNames; diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp index 1f539ee082..50b5db897c 100644 --- a/src/declarative/qml/qdeclarativeengine.cpp +++ b/src/declarative/qml/qdeclarativeengine.cpp @@ -542,6 +542,9 @@ QDeclarativeEngine::~QDeclarativeEngine() // ensure we clean up QObjects with JS ownership d->v8engine()->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 05c73da3a2..80bdfac24a 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<IP, &IP::recursion> 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 eaa4ce5ecb..ebabbae1d2 100644 --- a/src/declarative/qml/qdeclarativeincubator_p.h +++ b/src/declarative/qml/qdeclarativeincubator_p.h @@ -46,6 +46,7 @@ #include <private/qdeclarativevme_p.h> #include <private/qrecursionwatcher_p.h> #include <private/qdeclarativeengine_p.h> +#include <private/qdeclarativecontext_p.h> // // 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 b7abef4aef..ad8d80c51a 100644 --- a/src/declarative/qml/qdeclarativevme.cpp +++ b/src/declarative/qml/qdeclarativevme.cpp @@ -310,7 +310,7 @@ QObject *QDeclarativeVME::run(QList<QDeclarativeError> *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<QDeclarativeVME, &QDeclarativeVME::recursion> 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<QDeclarativeAbstractBinding *> &bs) diff --git a/src/declarative/qml/qdeclarativevme_p.h b/src/declarative/qml/qdeclarativevme_p.h index b74bc547bf..4edceb283a 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<QDeclarativeError> *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 0000000000..e79fed356a --- /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 99d2cb1005..6d6fb38daf 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<SelfRegisteringType>("Qt.test", 1,0, "SelfRegistering"); qmlRegisterType<CompletionRegisteringType>("Qt.test", 1,0, "CompletionRegistering"); qmlRegisterType<CallbackRegisteringType>("Qt.test", 1,0, "CallbackRegistering"); + qmlRegisterType<CompletionCallbackType>("Qt.test", 1,0, "CompletionCallback"); } diff --git a/tests/auto/declarative/qdeclarativeincubator/testtypes.h b/tests/auto/declarative/qdeclarativeincubator/testtypes.h index 6e732548b2..8d9968de3c 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 684cd35613..54ca622753 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 { |