aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Kennedy <aaron.kennedy@nokia.com>2011-11-02 14:28:02 +0000
committerQt by Nokia <qt-info@nokia.com>2011-11-04 11:27:52 +0100
commitf9261feb16d02e985982dd46783ea54c2cfce91b (patch)
tree205743bcd164628f882aa35580fd136384a28acb
parent0dd867535bebf6db673d4f03959e62e94ff35ba2 (diff)
Skip the captured properties step in bindings
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>
-rw-r--r--src/declarative/qml/ftw/ftw.pri1
-rw-r--r--src/declarative/qml/ftw/qfieldlist_p.h1
-rw-r--r--src/declarative/qml/ftw/qrecyclepool_p.h220
-rw-r--r--src/declarative/qml/qdeclarativeengine.cpp2
-rw-r--r--src/declarative/qml/qdeclarativeengine_p.h51
-rw-r--r--src/declarative/qml/qdeclarativeexpression.cpp132
-rw-r--r--src/declarative/qml/qdeclarativeexpression_p.h84
-rw-r--r--src/declarative/qml/v4/qv4compiler.cpp8
-rw-r--r--src/declarative/qml/v4/qv4compiler_p.h1
-rw-r--r--src/declarative/qml/v8/qv8contextwrapper.cpp10
-rw-r--r--src/declarative/qml/v8/qv8qobjectwrapper.cpp20
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/deleteWhileBindingRunning.qml5
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/nonNotifyable.qml6
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp1
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/testtypes.h20
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp46
16 files changed, 476 insertions, 132 deletions
diff --git a/src/declarative/qml/ftw/ftw.pri b/src/declarative/qml/ftw/ftw.pri
index c7750468d9..63399927e7 100644
--- a/src/declarative/qml/ftw/ftw.pri
+++ b/src/declarative/qml/ftw/ftw.pri
@@ -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 \
diff --git a/src/declarative/qml/ftw/qfieldlist_p.h b/src/declarative/qml/ftw/qfieldlist_p.h
index f0efd16a20..c6b432324c 100644
--- a/src/declarative/qml/ftw/qfieldlist_p.h
+++ b/src/declarative/qml/ftw/qfieldlist_p.h
@@ -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
index 0000000000..f349996a22
--- /dev/null
+++ b/src/declarative/qml/ftw/qrecyclepool_p.h
@@ -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
diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp
index 86c944f65e..1323669fc7 100644
--- a/src/declarative/qml/qdeclarativeengine.cpp
+++ b/src/declarative/qml/qdeclarativeengine.cpp
@@ -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),
diff --git a/src/declarative/qml/qdeclarativeengine_p.h b/src/declarative/qml/qdeclarativeengine_p.h
index 88eda390e3..a678a4588c 100644
--- a/src/declarative/qml/qdeclarativeengine_p.h
+++ b/src/declarative/qml/qdeclarativeengine_p.h
@@ -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
diff --git a/src/declarative/qml/qdeclarativeexpression.cpp b/src/declarative/qml/qdeclarativeexpression.cpp
index bfa9c58d86..fa899cf814 100644
--- a/src/declarative/qml/qdeclarativeexpression.cpp
+++ b/src/declarative/qml/qdeclarativeexpression.cpp
@@ -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)
{
diff --git a/src/declarative/qml/qdeclarativeexpression_p.h b/src/declarative/qml/qdeclarativeexpression_p.h
index 4e31efb806..65b07ca7e8 100644
--- a/src/declarative/qml/qdeclarativeexpression_p.h
+++ b/src/declarative/qml/qdeclarativeexpression_p.h
@@ -55,10 +55,10 @@
#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
diff --git a/src/declarative/qml/v4/qv4compiler.cpp b/src/declarative/qml/v4/qv4compiler.cpp
index a9d2338307..857bceb460 100644
--- a/src/declarative/qml/v4/qv4compiler.cpp
+++ b/src/declarative/qml/v4/qv4compiler.cpp
@@ -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
diff --git a/src/declarative/qml/v4/qv4compiler_p.h b/src/declarative/qml/v4/qv4compiler_p.h
index 9fafaf95af..cb5225a9cf 100644
--- a/src/declarative/qml/v4/qv4compiler_p.h
+++ b/src/declarative/qml/v4/qv4compiler_p.h
@@ -92,6 +92,7 @@ public:
static void dump(const QByteArray &);
static void enableBindingsTest(bool);
+ static void enableV4(bool);
private:
QV4CompilerPrivate *d;
};
diff --git a/src/declarative/qml/v8/qv8contextwrapper.cpp b/src/declarative/qml/v8/qv8contextwrapper.cpp
index 29857b2585..46cf0e4298 100644
--- a/src/declarative/qml/v8/qv8contextwrapper.cpp
+++ b/src/declarative/qml/v8/qv8contextwrapper.cpp
@@ -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*> >()) {
diff --git a/src/declarative/qml/v8/qv8qobjectwrapper.cpp b/src/declarative/qml/v8/qv8qobjectwrapper.cpp
index 40cb021512..eea455d332 100644
--- a/src/declarative/qml/v8/qv8qobjectwrapper.cpp
+++ b/src/declarative/qml/v8/qv8qobjectwrapper.cpp
@@ -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
index 0000000000..b5cc59e2c0
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/deleteWhileBindingRunning.qml
@@ -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
index 0000000000..2b8b113c34
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/nonNotifyable.qml
@@ -0,0 +1,6 @@
+import Qt.test 1.0
+
+MyQmlObject {
+ id: root
+ property int test: root.value
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp b/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
index 781b78663f..9213b61a3c 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
+++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
@@ -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
diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
index af4225c8da..9fbbd5052b 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
+++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
@@ -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
diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
index ee228ec3b8..57e4fe1a46 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
+++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
@@ -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"