Ensure that chained incubation works from componentCompleted.
authorMartin Jones <martin.jones@nokia.com>
Tue, 1 Nov 2011 00:05:47 +0000 (10:05 +1000)
committerQt by Nokia <qt-info@nokia.com>
Tue, 1 Nov 2011 12:09:31 +0000 (13:09 +0100)
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>
src/declarative/qml/qdeclarativecontext.cpp
src/declarative/qml/qdeclarativecontext_p.h
src/declarative/qml/qdeclarativeengine.cpp
src/declarative/qml/qdeclarativeincubator.cpp
src/declarative/qml/qdeclarativeincubator_p.h
src/declarative/qml/qdeclarativevme.cpp
src/declarative/qml/qdeclarativevme_p.h
tests/auto/declarative/qdeclarativeincubator/data/chainInCompletion.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeincubator/testtypes.cpp
tests/auto/declarative/qdeclarativeincubator/testtypes.h
tests/auto/declarative/qdeclarativeincubator/tst_qdeclarativeincubator.cpp

index c9ae825..c4fc764 100644 (file)
@@ -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)
index ba4edb8..a6e83a0 100644 (file)
@@ -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;
index 1f539ee..50b5db8 100644 (file)
@@ -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()
index 05c73da..80bdfac 100644 (file)
@@ -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);
     }
-
 }
 
 /*!
index eaa4ce5..ebabbae 100644 (file)
@@ -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;
index b7abef4..ad8d80c 100644 (file)
@@ -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)
index b74bc54..4edceb2 100644 (file)
@@ -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 (file)
index 0000000..e79fed3
--- /dev/null
@@ -0,0 +1,5 @@
+import Qt.test 1.0
+
+SelfRegistering {
+    property variant a: CompletionCallback {}
+}
index 99d2cb1..6d6fb38 100644 (file)
@@ -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");
 }
index 6e73254..8d9968d 100644 (file)
@@ -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
index 684cd35..54ca622 100644 (file)
@@ -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 {