diff options
-rw-r--r-- | src/declarative/qml/ftw/ftw.pri | 1 | ||||
-rw-r--r-- | src/declarative/qml/ftw/qfieldlist_p.h | 1 | ||||
-rw-r--r-- | src/declarative/qml/ftw/qrecyclepool_p.h | 220 | ||||
-rw-r--r-- | src/declarative/qml/qdeclarativeengine.cpp | 2 | ||||
-rw-r--r-- | src/declarative/qml/qdeclarativeengine_p.h | 51 | ||||
-rw-r--r-- | src/declarative/qml/qdeclarativeexpression.cpp | 132 | ||||
-rw-r--r-- | src/declarative/qml/qdeclarativeexpression_p.h | 84 | ||||
-rw-r--r-- | src/declarative/qml/v4/qv4compiler.cpp | 8 | ||||
-rw-r--r-- | src/declarative/qml/v4/qv4compiler_p.h | 1 | ||||
-rw-r--r-- | src/declarative/qml/v8/qv8contextwrapper.cpp | 10 | ||||
-rw-r--r-- | src/declarative/qml/v8/qv8qobjectwrapper.cpp | 20 | ||||
-rw-r--r-- | tests/auto/declarative/qdeclarativeecmascript/data/deleteWhileBindingRunning.qml | 5 | ||||
-rw-r--r-- | tests/auto/declarative/qdeclarativeecmascript/data/nonNotifyable.qml | 6 | ||||
-rw-r--r-- | tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp | 1 | ||||
-rw-r--r-- | tests/auto/declarative/qdeclarativeecmascript/testtypes.h | 20 | ||||
-rw-r--r-- | tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp | 46 |
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" |