Skip the captured properties step in bindings
authorAaron Kennedy <aaron.kennedy@nokia.com>
Wed, 2 Nov 2011 14:28:02 +0000 (14:28 +0000)
committerQt by Nokia <qt-info@nokia.com>
Fri, 4 Nov 2011 10:27:52 +0000 (11:27 +0100)
Objects and notifiers in the capturedProperties list were not guarded
which can lead to crashes if they're deleted prior to the binding
completing.  Now the notifiers are connected to and guarded immediately
to prevent this.

Change-Id: I912e323c52bf6169fb5077e552d5d38d9aa7faec
Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
16 files changed:
src/declarative/qml/ftw/ftw.pri
src/declarative/qml/ftw/qfieldlist_p.h
src/declarative/qml/ftw/qrecyclepool_p.h [new file with mode: 0644]
src/declarative/qml/qdeclarativeengine.cpp
src/declarative/qml/qdeclarativeengine_p.h
src/declarative/qml/qdeclarativeexpression.cpp
src/declarative/qml/qdeclarativeexpression_p.h
src/declarative/qml/v4/qv4compiler.cpp
src/declarative/qml/v4/qv4compiler_p.h
src/declarative/qml/v8/qv8contextwrapper.cpp
src/declarative/qml/v8/qv8qobjectwrapper.cpp
tests/auto/declarative/qdeclarativeecmascript/data/deleteWhileBindingRunning.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/nonNotifyable.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
tests/auto/declarative/qdeclarativeecmascript/testtypes.h
tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp

index c775046..6339992 100644 (file)
@@ -11,6 +11,7 @@ HEADERS +=  \
     $$PWD/qdeclarativethread_p.h \
     $$PWD/qfinitestack_p.h \
     $$PWD/qrecursionwatcher_p.h \
+    $$PWD/qrecyclepool_p.h \
 
 SOURCES += \
     $$PWD/qintrusivelist.cpp \
index f0efd16..c6b4323 100644 (file)
@@ -107,6 +107,7 @@ N *QFieldList<N, nextMember>::takeFirst()
             Q_ASSERT(_first == 0);
             _last = 0;
         }
+        value->*nextMember = 0;
         --_count;
     } 
     return value;
diff --git a/src/declarative/qml/ftw/qrecyclepool_p.h b/src/declarative/qml/ftw/qrecyclepool_p.h
new file mode 100644 (file)
index 0000000..f349996
--- /dev/null
@@ -0,0 +1,220 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QRECYCLEPOOL_P_H
+#define QRECYCLEPOOL_P_H
+
+//
+//  W A R N I N G
+//  -------------
+//
+// This file is not part of the Qt API.  It exists purely as an
+// implementation detail.  This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+#define QRECYCLEPOOLCOOKIE 0x33218ADF
+
+template<typename T, int Step>
+class QRecyclePoolPrivate
+{
+public:
+    QRecyclePoolPrivate()
+    : recyclePoolHold(true), outstandingItems(0), cookie(QRECYCLEPOOLCOOKIE),
+      currentPage(0), nextAllocated(0)
+    {
+    }
+
+    bool recyclePoolHold;
+    int outstandingItems;
+    quint32 cookie;
+
+    struct PoolType : public T {
+        union {
+            QRecyclePoolPrivate<T, Step> *pool;
+            PoolType *nextAllocated;
+        };
+    };
+
+    struct Page {
+        Page *nextPage;
+        unsigned int free;
+        union {
+            char array[Step * sizeof(PoolType)];
+            qint64 q_for_alignment_1;
+            double q_for_alignment_2;
+        };
+    };
+
+    Page *currentPage;
+    PoolType *nextAllocated;
+
+    inline T *allocate();
+    static inline void dispose(T *);
+    inline void releaseIfPossible();
+};
+
+template<typename T, int Step = 1024>
+class QRecyclePool
+{
+public:
+    inline QRecyclePool();
+    inline ~QRecyclePool();
+
+    inline T *New();
+    template<typename T1>
+    inline T *New(const T1 &);
+    template<typename T1>
+    inline T *New(T1 &);
+
+    static inline void Delete(T *);
+
+private:
+    QRecyclePoolPrivate<T, Step> *d;
+};
+
+template<typename T, int Step>
+QRecyclePool<T, Step>::QRecyclePool()
+: d(new QRecyclePoolPrivate<T, Step>())
+{
+}
+
+template<typename T, int Step>
+QRecyclePool<T, Step>::~QRecyclePool()
+{
+    d->recyclePoolHold = false;
+    d->releaseIfPossible();
+}
+
+template<typename T, int Step>
+T *QRecyclePool<T, Step>::New()
+{
+    T *rv = d->allocate();
+    new (rv) T;
+    return rv;
+}
+
+template<typename T, int Step>
+template<typename T1>
+T *QRecyclePool<T, Step>::New(const T1 &a)
+{
+    T *rv = d->allocate();
+    new (rv) T(a);
+    return rv;
+}
+
+template<typename T, int Step>
+template<typename T1>
+T *QRecyclePool<T, Step>::New(T1 &a)
+{
+    T *rv = d->allocate();
+    new (rv) T(a);
+    return rv;
+}
+
+template<typename T, int Step>
+void QRecyclePool<T, Step>::Delete(T *t)
+{
+    t->~T();
+    QRecyclePoolPrivate<T, Step>::dispose(t);
+}
+
+template<typename T, int Step>
+void QRecyclePoolPrivate<T, Step>::releaseIfPossible()
+{
+    if (recyclePoolHold || outstandingItems)
+        return;
+
+    Page *p = currentPage;
+    while (p) {
+        Page *n = p->nextPage;
+        qFree(p);
+        p = n;
+    }
+
+    delete this;
+}
+
+template<typename T, int Step>
+T *QRecyclePoolPrivate<T, Step>::allocate()
+{
+    PoolType *rv = 0;
+    if (nextAllocated) {
+        rv = nextAllocated;
+        nextAllocated = rv->nextAllocated;
+    } else if (currentPage && currentPage->free) {
+        rv = (PoolType *)(currentPage->array + (Step - currentPage->free) * sizeof(PoolType));
+        currentPage->free--;
+    } else {
+        Page *p = (Page *)qMalloc(sizeof(Page));
+        p->nextPage = currentPage;
+        p->free = Step;
+        currentPage = p;
+
+        rv = (PoolType *)currentPage->array;
+        currentPage->free--;
+    }
+
+    rv->pool = this;
+    ++outstandingItems;
+    return rv;
+}
+
+template<typename T, int Step>
+void QRecyclePoolPrivate<T, Step>::dispose(T *t)
+{
+    PoolType *pt = static_cast<PoolType *>(t);
+    Q_ASSERT(pt->pool && pt->pool->cookie == QRECYCLEPOOLCOOKIE);
+
+    QRecyclePoolPrivate<T, Step> *This = pt->pool;
+    pt->nextAllocated = This->nextAllocated;
+    This->nextAllocated = pt;
+    --This->outstandingItems;
+    This->releaseIfPossible();
+}
+
+QT_END_NAMESPACE
+
+#endif // QRECYCLEPOOL_P_H
index 86c944f..1323669 100644 (file)
@@ -332,7 +332,7 @@ the same object as is returned from the Qt.include() call.
 
 
 QDeclarativeEnginePrivate::QDeclarativeEnginePrivate(QDeclarativeEngine *e)
-: captureProperties(false), rootContext(0), isDebugging(false),
+: propertyCapture(0), rootContext(0), isDebugging(false),
   outputWarningsToStdErr(true), sharedContext(0), sharedScope(0),
   cleanup(0), erroredBindings(0), inProgressCreations(0), 
   workerScriptEngine(0), activeVME(0),
index 88eda39..a678a45 100644 (file)
@@ -69,6 +69,7 @@
 #include "qdeclarativemetatype_p.h"
 #include "qdeclarativedirparser_p.h"
 #include <private/qintrusivelist_p.h>
+#include <private/qrecyclepool_p.h>
 
 #include <QtCore/qlist.h>
 #include <QtCore/qpair.h>
@@ -103,6 +104,21 @@ class QSGTexture;
 class QDeclarativeIncubator;
 class QSGContext;
 
+// This needs to be declared here so that the pool for it can live in QDeclarativeEnginePrivate.
+// The inline method definitions are in qdeclarativeexpression_p.h
+class QDeclarativeJavaScriptExpressionGuard : public QDeclarativeNotifierEndpoint
+{
+public:
+    inline QDeclarativeJavaScriptExpressionGuard(QDeclarativeJavaScriptExpression *);
+
+    static inline void endpointCallback(QDeclarativeNotifierEndpoint *);
+    static inline QDeclarativeJavaScriptExpressionGuard *New(QDeclarativeJavaScriptExpression *e);
+    inline void Delete();
+
+    QDeclarativeJavaScriptExpression *expression;
+    QDeclarativeJavaScriptExpressionGuard *next;
+};
+
 class Q_DECLARATIVE_EXPORT QDeclarativeEnginePrivate : public QObjectPrivate
 {
     Q_DECLARE_PUBLIC(QDeclarativeEngine)
@@ -112,19 +128,18 @@ public:
 
     void init();
 
-    struct CapturedProperty {
-        CapturedProperty(QObject *o, int c, int n)
-            : object(o), coreIndex(c), notifier(0), notifyIndex(n) {}
-        CapturedProperty(QDeclarativeNotifier *n)
-            : object(0), coreIndex(-1), notifier(n), notifyIndex(-1) {}
-
-        QObject *object;
-        int coreIndex;
-        QDeclarativeNotifier *notifier;
-        int notifyIndex;
+    class PropertyCapture {
+    public:
+        inline virtual ~PropertyCapture() {}
+        virtual void captureProperty(QDeclarativeNotifier *) = 0;
+        virtual void captureProperty(QObject *, int, int) = 0;
     };
-    bool captureProperties;
-    QPODVector<CapturedProperty> capturedProperties;
+
+    PropertyCapture *propertyCapture;
+    inline void captureProperty(QDeclarativeNotifier *);
+    inline void captureProperty(QObject *, int, int);
+
+    QRecyclePool<QDeclarativeJavaScriptExpressionGuard> jsExpressionGuardPool;
 
     QDeclarativeContext *rootContext;
     bool isDebugging;
@@ -492,6 +507,18 @@ QDeclarativeEngine *QDeclarativeEnginePrivate::get(QDeclarativeEnginePrivate *p)
     return p->q_func(); 
 }
 
+void QDeclarativeEnginePrivate::captureProperty(QDeclarativeNotifier *n)
+{
+    if (propertyCapture)
+        propertyCapture->captureProperty(n);
+}
+
+void QDeclarativeEnginePrivate::captureProperty(QObject *o, int c, int n)
+{
+    if (propertyCapture)
+        propertyCapture->captureProperty(o, c, n);
+}
+
 QT_END_NAMESPACE
 
 #endif // QDECLARATIVEENGINE_P_H
index bfa9c58..fa899cf 100644 (file)
@@ -70,12 +70,14 @@ bool QDeclarativeDelayedError::addError(QDeclarativeEnginePrivate *e)
 
 QDeclarativeJavaScriptExpression::QDeclarativeJavaScriptExpression()
 : m_requiresThisObject(0), m_useSharedContext(0), m_notifyOnValueChanged(0), 
-  m_scopeObject(0)
+  m_scopeObject(0), guardCapture(0)
 {
 }
 
 QDeclarativeJavaScriptExpression::~QDeclarativeJavaScriptExpression()
 {
+    if (guardCapture) guardCapture->expression = 0;
+    clearGuards();
 }
 
 QDeclarativeExpressionPrivate::QDeclarativeExpressionPrivate()
@@ -396,12 +398,12 @@ void QDeclarativeExpressionPrivate::exceptionToError(v8::Handle<v8::Message> mes
 void QDeclarativeJavaScriptExpression::setNotifyOnValueChanged(bool v)
 {
     m_notifyOnValueChanged = v;
-    if (!v) guardList.clear();
+    if (!v) clearGuards();
 }
 
 void QDeclarativeJavaScriptExpression::resetNotifyOnValueChanged()
 {
-    guardList.clear();
+    clearGuards();
 }
 
 v8::Local<v8::Value> QDeclarativeJavaScriptExpression::evaluate(v8::Handle<v8::Function> function, bool *isUndefined)
@@ -415,12 +417,15 @@ v8::Local<v8::Value> QDeclarativeJavaScriptExpression::evaluate(v8::Handle<v8::F
 
     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context()->engine);
 
-    bool lastCaptureProperties = ep->captureProperties;
-    QPODVector<QDeclarativeEnginePrivate::CapturedProperty> lastCapturedProperties;
-    ep->captureProperties = notifyOnValueChanged();
+    Q_ASSERT(notifyOnValueChanged() || activeGuards.isEmpty());
+    GuardCapture capture(this);
 
-    if (ep->capturedProperties.count())
-        ep->capturedProperties.copyAndClear(lastCapturedProperties);
+    QDeclarativeEnginePrivate::PropertyCapture *lastPropertyCapture = ep->propertyCapture;
+    ep->propertyCapture = notifyOnValueChanged()?&capture:0;
+
+
+    if (notifyOnValueChanged())
+        capture.guards.copyAndClear(activeGuards);
 
     QDeclarativeContextData *lastSharedContext = 0;
     QObject *lastSharedScope = 0;
@@ -471,79 +476,90 @@ v8::Local<v8::Value> QDeclarativeJavaScriptExpression::evaluate(v8::Handle<v8::F
         ep->sharedScope = lastSharedScope;
     }
 
-    if (!watcher.wasDeleted() && notifyOnValueChanged()) {
-        guardList.updateGuards(this, ep->capturedProperties);
+    if (capture.errorString) {
+        for (int ii = 0; ii < capture.errorString->count(); ++ii)
+            qWarning("%s", qPrintable(capture.errorString->at(ii)));
+        delete capture.errorString;
+        capture.errorString = 0;
     }
 
-    if (lastCapturedProperties.count())
-        lastCapturedProperties.copyAndClear(ep->capturedProperties);
-    else
-        ep->capturedProperties.clear();
+    while (Guard *g = capture.guards.takeFirst())
+        g->Delete();
 
-    ep->captureProperties = lastCaptureProperties;
+    ep->propertyCapture = lastPropertyCapture;
 
     return result;
 }
 
-void 
-QDeclarativeJavaScriptExpression::GuardList::updateGuards(QDeclarativeJavaScriptExpression *expression,
-                                                          const CapturedProperties &properties)
+void QDeclarativeJavaScriptExpression::GuardCapture::captureProperty(QDeclarativeNotifier *n)
 {
-    if (properties.count() == 0) {
-        clear();
-        return;
-    }
+    if (expression) {
 
-    if (properties.count() != length) {
-        Endpoint *newGuardList = new Endpoint[properties.count()];
+        // Try and find a matching guard
+        while (!guards.isEmpty() && !guards.first()->isConnected(n))
+            guards.takeFirst()->Delete();
 
-        for (int ii = 0; ii < qMin(length, properties.count()); ++ii) 
-           endpoints[ii].copyAndClear(newGuardList[ii]);
+        Guard *g = 0;
+        if (!guards.isEmpty()) {
+            g = guards.takeFirst();
+            g->cancelNotify();
+            Q_ASSERT(g->isConnected(n));
+        } else {
+            g = Guard::New(expression);
+            g->connect(n);
+        }
 
-        delete [] endpoints;
-        endpoints = newGuardList;
-        length = properties.count();
+        expression->activeGuards.append(g);
     }
+}
 
-    bool outputWarningHeader = false;
-    for (int ii = 0; ii < properties.count(); ++ii) {
-        Endpoint &guard = endpoints[ii];
-        const QDeclarativeEnginePrivate::CapturedProperty &property = properties.at(ii);
-
-        guard.expression = expression;
-
-        if (property.notifier != 0) {
-
-            if (guard.isConnected(property.notifier)) {
-                 guard.cancelNotify();
-            } else {
-                guard.connect(property.notifier);
+void QDeclarativeJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, int n)
+{
+    if (expression) {
+        if (n == -1) {
+            if (!errorString) {
+                errorString = new QStringList;
+                QString preamble = QLatin1String("QDeclarativeExpression: Expression ") +
+                                   expression->expressionIdentifier() +
+                                   QLatin1String(" depends on non-NOTIFYable properties:");
+                errorString->append(preamble);
             }
 
-        } else if (property.notifyIndex != -1) {
-
-            if (guard.isConnected(property.object, property.notifyIndex)) {
-                guard.cancelNotify();
-            } else { 
-                guard.connect(property.object, property.notifyIndex);
-            }
+            const QMetaObject *metaObj = o->metaObject();
+            QMetaProperty metaProp = metaObj->property(c);
 
+            QString error = QLatin1String("    ") +
+                            QString::fromUtf8(metaObj->className()) +
+                            QLatin1String("::") +
+                            QString::fromUtf8(metaProp.name());
+            errorString->append(error);
         } else {
-            if (!outputWarningHeader) {
-                QString e = expression->expressionIdentifier();
-                outputWarningHeader = true;
-                qWarning() << "QDeclarativeExpression: Expression" << qPrintable(e)
-                           << "depends on non-NOTIFYable properties:";
-            }
 
-            const QMetaObject *metaObj = property.object->metaObject();
-            QMetaProperty metaProp = metaObj->property(property.coreIndex);
+            // Try and find a matching guard
+            while (!guards.isEmpty() && !guards.first()->isConnected(o, n))
+                guards.takeFirst()->Delete();
+
+            Guard *g = 0;
+            if (!guards.isEmpty()) {
+                g = guards.takeFirst();
+                g->cancelNotify();
+                Q_ASSERT(g->isConnected(o, n));
+            } else {
+                g = Guard::New(expression);
+                g->connect(o, n);
+            }
 
-            qWarning().nospace() << "    " << metaObj->className() << "::" << metaProp.name();
+            expression->activeGuards.append(g);
         }
     }
 }
 
+void QDeclarativeJavaScriptExpression::clearGuards()
+{
+    while (Guard *g = activeGuards.takeFirst())
+        g->Delete();
+}
+
 // Must be called with a valid handle scope
 v8::Local<v8::Value> QDeclarativeExpressionPrivate::v8value(QObject *secondaryScope, bool *isUndefined)
 {
index 4e31efb..65b07ca 100644 (file)
 
 #include "qdeclarativeexpression.h"
 
-#include <private/qdeclarativeengine_p.h>
-#include <private/qdeclarativeguard_p.h>
-
 #include <private/qv8engine_p.h>
+#include <private/qfieldlist_p.h>
+#include <private/qdeclarativeguard_p.h>
+#include <private/qdeclarativeengine_p.h>
 
 QT_BEGIN_NAMESPACE
 
@@ -164,28 +164,28 @@ private:
 
     QObject *m_scopeObject;
 
-    class GuardList {
-    public:
-        inline GuardList();
-        inline ~GuardList();
-        void inline clear();
-
-        typedef QPODVector<QDeclarativeEnginePrivate::CapturedProperty> CapturedProperties;
-        void updateGuards(QDeclarativeJavaScriptExpression *, const CapturedProperties &properties);
-
-    private:
-        struct Endpoint : public QDeclarativeNotifierEndpoint {
-            Endpoint() : expression(0) { callback = &endpointCallback; }
-            static void endpointCallback(QDeclarativeNotifierEndpoint *e) { 
-                static_cast<Endpoint *>(e)->expression->expressionChanged(); 
-            }
-            QDeclarativeJavaScriptExpression *expression;
-        };
-
-        Endpoint *endpoints;
-        int length;
+    typedef QDeclarativeJavaScriptExpressionGuard Guard;
+
+    struct GuardCapture : public QDeclarativeEnginePrivate::PropertyCapture {
+        GuardCapture(QDeclarativeJavaScriptExpression *e) : expression(e), errorString(0) {
+        }
+        ~GuardCapture()  {
+            Q_ASSERT(guards.isEmpty());
+            Q_ASSERT(errorString == 0);
+        }
+
+        virtual void captureProperty(QDeclarativeNotifier *);
+        virtual void captureProperty(QObject *, int, int);
+
+        QDeclarativeJavaScriptExpression *expression;
+        QFieldList<Guard, &Guard::next> guards;
+        QStringList *errorString;
     };
-    GuardList guardList;
+
+    QFieldList<Guard, &Guard::next> activeGuards;
+    GuardCapture *guardCapture;
+
+    void clearGuards();
 };
 
 class QDeclarativeExpression;
@@ -302,36 +302,42 @@ QString QDeclarativeJavaScriptExpression::expressionIdentifier()
     return QString();
 }
 
-QDeclarativeJavaScriptExpression::GuardList::GuardList() 
-: endpoints(0), length(0) 
+QDeclarativeExpressionPrivate *QDeclarativeExpressionPrivate::get(QDeclarativeExpression *expr)
 {
+    return static_cast<QDeclarativeExpressionPrivate *>(QObjectPrivate::get(expr));
 }
 
-QDeclarativeJavaScriptExpression::GuardList::~GuardList() 
-{ 
-    clear(); 
+QDeclarativeExpression *QDeclarativeExpressionPrivate::get(QDeclarativeExpressionPrivate *expr)
+{
+    return expr->q_func();
 }
 
-void QDeclarativeJavaScriptExpression::GuardList::clear() 
+QString QDeclarativeExpressionPrivate::expressionIdentifier()
+{
+    return QLatin1String("\"") + expression + QLatin1String("\"");
+}
+
+QDeclarativeJavaScriptExpressionGuard::QDeclarativeJavaScriptExpressionGuard(QDeclarativeJavaScriptExpression *e)
+: expression(e), next(0)
 { 
-    delete [] endpoints; 
-    endpoints = 0; 
-    length = 0; 
+    callback = &endpointCallback;
 }
 
-QDeclarativeExpressionPrivate *QDeclarativeExpressionPrivate::get(QDeclarativeExpression *expr) 
+void QDeclarativeJavaScriptExpressionGuard::endpointCallback(QDeclarativeNotifierEndpoint *e)
 {
-    return static_cast<QDeclarativeExpressionPrivate *>(QObjectPrivate::get(expr));
+    static_cast<QDeclarativeJavaScriptExpressionGuard *>(e)->expression->expressionChanged();
 }
 
-QDeclarativeExpression *QDeclarativeExpressionPrivate::get(QDeclarativeExpressionPrivate *expr) 
+QDeclarativeJavaScriptExpressionGuard *
+QDeclarativeJavaScriptExpressionGuard::New(QDeclarativeJavaScriptExpression *e)
 {
-    return expr->q_func();
+    Q_ASSERT(e);
+    return QDeclarativeEnginePrivate::get(e->context()->engine)->jsExpressionGuardPool.New(e);
 }
 
-QString QDeclarativeExpressionPrivate::expressionIdentifier()
+void QDeclarativeJavaScriptExpressionGuard::Delete()
 {
-    return QLatin1String("\"") + expression + QLatin1String("\"");
+    QRecyclePool<QDeclarativeJavaScriptExpressionGuard>::Delete(this);
 }
 
 QT_END_NAMESPACE
index a9d2338..857bceb 100644 (file)
@@ -59,6 +59,7 @@ DEFINE_BOOL_CONFIG_OPTION(qmlVerboseCompiler, QML_VERBOSE_COMPILER)
 DEFINE_BOOL_CONFIG_OPTION(qmlBindingsTestEnv, QML_BINDINGS_TEST)
 
 static bool qmlBindingsTest = false;
+static bool qmlEnableV4 = true;
 
 using namespace QDeclarativeJS;
 QV4CompilerPrivate::QV4CompilerPrivate()
@@ -1219,7 +1220,7 @@ int QV4Compiler::compile(const Expression &expression, QDeclarativeEnginePrivate
     if (!qmlExperimental() && expression.property->isValueTypeSubProperty)
         return -1;
 
-    if (qmlDisableOptimizer())
+    if (qmlDisableOptimizer() || !qmlEnableV4)
         return -1;
 
     d->expression = &expression;
@@ -1341,4 +1342,9 @@ void QV4Compiler::enableBindingsTest(bool e)
         qmlBindingsTest = qmlBindingsTestEnv();
 }
 
+void QV4Compiler::enableV4(bool e)
+{
+    qmlEnableV4 = e;
+}
+
 QT_END_NAMESPACE
index 9fafaf9..cb5225a 100644 (file)
@@ -92,6 +92,7 @@ public:
 
     static void dump(const QByteArray &);
     static void enableBindingsTest(bool);
+    static void enableV4(bool);
 private:
     QV4CompilerPrivate *d;
 };
index 29857b2..46cf0e4 100644 (file)
@@ -317,21 +317,17 @@ v8::Handle<v8::Value> QV8ContextWrapper::Getter(v8::Local<v8::String> property,
             int propertyIdx = context->propertyNames->value(propertystring);
 
             if (propertyIdx != -1) {
-                typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
 
                 if (propertyIdx < context->idValueCount) {
 
-                    if (ep->captureProperties)
-                        ep->capturedProperties << CapturedProperty(&context->idValues[propertyIdx].bindings);
-
+                    ep->captureProperty(&context->idValues[propertyIdx].bindings);
                     return engine->newQObject(context->idValues[propertyIdx]);
                 } else {
 
                     QDeclarativeContextPrivate *cp = context->asQDeclarativeContextPrivate();
 
-                    if (ep->captureProperties)
-                        ep->capturedProperties << CapturedProperty(context->asQDeclarativeContext(), -1, 
-                                                                   propertyIdx + cp->notifyIndex);
+                    ep->captureProperty(context->asQDeclarativeContext(), -1,
+                                        propertyIdx + cp->notifyIndex);
 
                     const QVariant &value = cp->propertyValues.at(propertyIdx);
                     if (value.userType() == qMetaTypeId<QList<QObject*> >()) {
index 40cb021..eea455d 100644 (file)
@@ -229,10 +229,8 @@ static v8::Handle<v8::Value> name ## ValueGetter(v8::Local<v8::String>, const v8
     if (notify == 0x0FFF) notify = -1; \
  \
     QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \
-    if (ep && notify /* 0 means constant */ && ep->captureProperties) { \
-        typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
-        ep->capturedProperties << CapturedProperty(object, index, notify); \
-    } \
+    if (ep && notify /* 0 means constant */ ) \
+        ep->captureProperty(object, index, notify); \
  \
     cpptype value = defaultvalue; \
     void *args[] = { &value, 0 }; \
@@ -255,10 +253,8 @@ static v8::Handle<v8::Value> name ## ValueGetterDirect(v8::Local<v8::String>, co
     if (notify == 0x0FFF) notify = -1; \
  \
     QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \
-    if (ep && notify /* 0 means constant */ && ep->captureProperties) { \
-        typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
-        ep->capturedProperties << CapturedProperty(object, index, notify); \
-    } \
+    if (ep && notify /* 0 means constant */ ) \
+        ep->captureProperty(object, index, notify); \
  \
     cpptype value = defaultvalue; \
     void *args[] = { &value, 0 }; \
@@ -508,8 +504,6 @@ v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject
             return v8::Handle<v8::Value>();
     }
 
-    typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
-
     if (result->isFunction()) {
         if (result->isVMEFunction()) {
             return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
@@ -526,11 +520,11 @@ v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject
     }
 
     QDeclarativeEnginePrivate *ep = engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0;
-    if (ep && ep->captureProperties && !result->isConstant()) {
+    if (ep && !result->isConstant()) {
         if (result->coreIndex == 0)
-            ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier());
+            ep->captureProperty(QDeclarativeData::get(object, true)->objectNameNotifier());
         else
-            ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex);
+            ep->captureProperty(object, result->coreIndex, result->notifyIndex);
     }
 
     if (result->isVMEProperty())
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/deleteWhileBindingRunning.qml b/tests/auto/declarative/qdeclarativeecmascript/data/deleteWhileBindingRunning.qml
new file mode 100644 (file)
index 0000000..b5cc59e
--- /dev/null
@@ -0,0 +1,5 @@
+import Qt.test 1.0
+
+MyDeleteObject {
+    property int result: nestedObject.intProperty + deleteNestedObject
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/nonNotifyable.qml b/tests/auto/declarative/qdeclarativeecmascript/data/nonNotifyable.qml
new file mode 100644 (file)
index 0000000..2b8b113
--- /dev/null
@@ -0,0 +1,6 @@
+import Qt.test 1.0
+
+MyQmlObject {
+    id: root
+    property int test: root.value
+}
index 781b786..9213b61 100644 (file)
@@ -161,6 +161,7 @@ void registerTypes()
     qmlRegisterExtendedType<DefaultPropertyExtendedObject, DefaultPropertyExtensionObject>("Qt.test", 1,0, "DefaultPropertyExtendedObject");
     qmlRegisterType<OverrideDefaultPropertyObject>("Qt.test", 1,0, "OverrideDefaultPropertyObject");
     qmlRegisterType<MyRevisionedClass>("Qt.test",1,0,"MyRevisionedClass");
+    qmlRegisterType<MyDeleteObject>("Qt.test", 1,0, "MyDeleteObject");
     qmlRegisterType<MyRevisionedClass,1>("Qt.test",1,1,"MyRevisionedClass");
 
     // test scarce resource property binding post-evaluation optimisation
index af4225c..9fbbd50 100644 (file)
@@ -1229,6 +1229,26 @@ private:
     QList<QPoint> m_pointList; // not a supported sequence type
 };
 
+class MyDeleteObject : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(QObject *nestedObject READ nestedObject NOTIFY nestedObjectChanged);
+    Q_PROPERTY(int deleteNestedObject READ deleteNestedObject NOTIFY deleteNestedObjectChanged);
+
+public:
+    MyDeleteObject() : m_nestedObject(new MyQmlObject) {}
+
+    QObject *nestedObject() const { return m_nestedObject; }
+    int deleteNestedObject() { delete m_nestedObject; m_nestedObject = 0; return 1; }
+
+signals:
+    void nestedObjectChanged();
+    void deleteNestedObjectChanged();
+
+private:
+    MyQmlObject *m_nestedObject;
+};
+
 void registerTypes();
 
 #endif // TESTTYPES_H
index ee228ec..57e4fe1 100644 (file)
@@ -51,6 +51,7 @@
 #include <private/qdeclarativeengine_p.h>
 #include <private/qv8gccallback_p.h>
 #include <private/qdeclarativevmemetaobject_p.h>
+#include <private/qv4compiler_p.h>
 #include "testtypes.h"
 #include "testhttpserver.h"
 #include "../shared/util.h"
@@ -216,7 +217,8 @@ private slots:
     void signalHandlers();
     void doubleEvaluate();
     void forInLoop();
-
+    void nonNotifyable();
+    void deleteWhileBindingRunning();
     void callQtInvokables();
     void invokableObjectArg();
     void invokableObjectRet();
@@ -5046,6 +5048,39 @@ void tst_qdeclarativeecmascript::doubleEvaluate()
     delete object;
 }
 
+static QStringList messages;
+static void captureMsgHandler(QtMsgType, const char *msg)
+{
+    messages.append(QLatin1String(msg));
+}
+
+void tst_qdeclarativeecmascript::nonNotifyable()
+{
+    QV4Compiler::enableV4(false);
+    QDeclarativeComponent component(&engine, TEST_FILE("nonNotifyable.qml"));
+    QV4Compiler::enableV4(true);
+
+    QtMsgHandler old = qInstallMsgHandler(captureMsgHandler);
+    messages.clear();
+    QObject *object = component.create();
+    qInstallMsgHandler(old);
+
+    QVERIFY(object != 0);
+
+    QString expected1 = QLatin1String("QDeclarativeExpression: Expression ") +
+                        component.url().toString() +
+                        QLatin1String(":5 depends on non-NOTIFYable properties:");
+    QString expected2 = QLatin1String("    ") +
+                        QLatin1String(object->metaObject()->className()) +
+                        QLatin1String("::value");
+
+    QCOMPARE(messages.length(), 2);
+    QCOMPARE(messages.at(0), expected1);
+    QCOMPARE(messages.at(1), expected2);
+
+    delete object;
+}
+
 void tst_qdeclarativeecmascript::forInLoop()
 {
     QDeclarativeComponent component(&engine, TEST_FILE("forInLoop.qml"));
@@ -5065,6 +5100,15 @@ void tst_qdeclarativeecmascript::forInLoop()
     delete object;
 }
 
+// An object the binding depends on is deleted while the binding is still running
+void tst_qdeclarativeecmascript::deleteWhileBindingRunning()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("deleteWhileBindingRunning.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    delete object;
+}
+
 QTEST_MAIN(tst_qdeclarativeecmascript)
 
 #include "tst_qdeclarativeecmascript.moc"