From f9261feb16d02e985982dd46783ea54c2cfce91b Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Wed, 2 Nov 2011 14:28:02 +0000 Subject: [PATCH] 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 --- src/declarative/qml/ftw/ftw.pri | 1 + src/declarative/qml/ftw/qfieldlist_p.h | 1 + src/declarative/qml/ftw/qrecyclepool_p.h | 220 +++++++++++++++++++++ src/declarative/qml/qdeclarativeengine.cpp | 2 +- src/declarative/qml/qdeclarativeengine_p.h | 51 +++-- src/declarative/qml/qdeclarativeexpression.cpp | 132 +++++++------ src/declarative/qml/qdeclarativeexpression_p.h | 84 ++++---- src/declarative/qml/v4/qv4compiler.cpp | 8 +- src/declarative/qml/v4/qv4compiler_p.h | 1 + src/declarative/qml/v8/qv8contextwrapper.cpp | 10 +- src/declarative/qml/v8/qv8qobjectwrapper.cpp | 20 +- .../data/deleteWhileBindingRunning.qml | 5 + .../qdeclarativeecmascript/data/nonNotifyable.qml | 6 + .../qdeclarativeecmascript/testtypes.cpp | 1 + .../declarative/qdeclarativeecmascript/testtypes.h | 20 ++ .../tst_qdeclarativeecmascript.cpp | 46 ++++- 16 files changed, 476 insertions(+), 132 deletions(-) create mode 100644 src/declarative/qml/ftw/qrecyclepool_p.h create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/deleteWhileBindingRunning.qml create mode 100644 tests/auto/declarative/qdeclarativeecmascript/data/nonNotifyable.qml diff --git a/src/declarative/qml/ftw/ftw.pri b/src/declarative/qml/ftw/ftw.pri index c775046..6339992 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 f0efd16..c6b4323 100644 --- a/src/declarative/qml/ftw/qfieldlist_p.h +++ b/src/declarative/qml/ftw/qfieldlist_p.h @@ -107,6 +107,7 @@ N *QFieldList::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 0000000..f349996 --- /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 +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 *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 +class QRecyclePool +{ +public: + inline QRecyclePool(); + inline ~QRecyclePool(); + + inline T *New(); + template + inline T *New(const T1 &); + template + inline T *New(T1 &); + + static inline void Delete(T *); + +private: + QRecyclePoolPrivate *d; +}; + +template +QRecyclePool::QRecyclePool() +: d(new QRecyclePoolPrivate()) +{ +} + +template +QRecyclePool::~QRecyclePool() +{ + d->recyclePoolHold = false; + d->releaseIfPossible(); +} + +template +T *QRecyclePool::New() +{ + T *rv = d->allocate(); + new (rv) T; + return rv; +} + +template +template +T *QRecyclePool::New(const T1 &a) +{ + T *rv = d->allocate(); + new (rv) T(a); + return rv; +} + +template +template +T *QRecyclePool::New(T1 &a) +{ + T *rv = d->allocate(); + new (rv) T(a); + return rv; +} + +template +void QRecyclePool::Delete(T *t) +{ + t->~T(); + QRecyclePoolPrivate::dispose(t); +} + +template +void QRecyclePoolPrivate::releaseIfPossible() +{ + if (recyclePoolHold || outstandingItems) + return; + + Page *p = currentPage; + while (p) { + Page *n = p->nextPage; + qFree(p); + p = n; + } + + delete this; +} + +template +T *QRecyclePoolPrivate::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 +void QRecyclePoolPrivate::dispose(T *t) +{ + PoolType *pt = static_cast(t); + Q_ASSERT(pt->pool && pt->pool->cookie == QRECYCLEPOOLCOOKIE); + + QRecyclePoolPrivate *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 86c944f..1323669 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 88eda39..a678a45 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 +#include #include #include @@ -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 capturedProperties; + + PropertyCapture *propertyCapture; + inline void captureProperty(QDeclarativeNotifier *); + inline void captureProperty(QObject *, int, int); + + QRecyclePool 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 bfa9c58..fa899cf 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 mes void QDeclarativeJavaScriptExpression::setNotifyOnValueChanged(bool v) { m_notifyOnValueChanged = v; - if (!v) guardList.clear(); + if (!v) clearGuards(); } void QDeclarativeJavaScriptExpression::resetNotifyOnValueChanged() { - guardList.clear(); + clearGuards(); } v8::Local QDeclarativeJavaScriptExpression::evaluate(v8::Handle function, bool *isUndefined) @@ -415,12 +417,15 @@ v8::Local QDeclarativeJavaScriptExpression::evaluate(v8::Handleengine); - bool lastCaptureProperties = ep->captureProperties; - QPODVector 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 QDeclarativeJavaScriptExpression::evaluate(v8::HandlesharedScope = 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 QDeclarativeExpressionPrivate::v8value(QObject *secondaryScope, bool *isUndefined) { diff --git a/src/declarative/qml/qdeclarativeexpression_p.h b/src/declarative/qml/qdeclarativeexpression_p.h index 4e31efb..65b07ca 100644 --- a/src/declarative/qml/qdeclarativeexpression_p.h +++ b/src/declarative/qml/qdeclarativeexpression_p.h @@ -55,10 +55,10 @@ #include "qdeclarativeexpression.h" -#include -#include - #include +#include +#include +#include QT_BEGIN_NAMESPACE @@ -164,28 +164,28 @@ private: QObject *m_scopeObject; - class GuardList { - public: - inline GuardList(); - inline ~GuardList(); - void inline clear(); - - typedef QPODVector CapturedProperties; - void updateGuards(QDeclarativeJavaScriptExpression *, const CapturedProperties &properties); - - private: - struct Endpoint : public QDeclarativeNotifierEndpoint { - Endpoint() : expression(0) { callback = &endpointCallback; } - static void endpointCallback(QDeclarativeNotifierEndpoint *e) { - static_cast(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 guards; + QStringList *errorString; }; - GuardList guardList; + + QFieldList 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(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(QObjectPrivate::get(expr)); + static_cast(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::Delete(this); } QT_END_NAMESPACE diff --git a/src/declarative/qml/v4/qv4compiler.cpp b/src/declarative/qml/v4/qv4compiler.cpp index a9d2338..857bceb 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 9fafaf9..cb5225a 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 29857b2..46cf0e4 100644 --- a/src/declarative/qml/v8/qv8contextwrapper.cpp +++ b/src/declarative/qml/v8/qv8contextwrapper.cpp @@ -317,21 +317,17 @@ v8::Handle QV8ContextWrapper::Getter(v8::Local 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 >()) { diff --git a/src/declarative/qml/v8/qv8qobjectwrapper.cpp b/src/declarative/qml/v8/qv8qobjectwrapper.cpp index 40cb021..eea455d 100644 --- a/src/declarative/qml/v8/qv8qobjectwrapper.cpp +++ b/src/declarative/qml/v8/qv8qobjectwrapper.cpp @@ -229,10 +229,8 @@ static v8::Handle name ## ValueGetter(v8::Local, 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 name ## ValueGetterDirect(v8::Local, 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 QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject return v8::Handle(); } - typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; - if (result->isFunction()) { if (result->isVMEFunction()) { return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex); @@ -526,11 +520,11 @@ v8::Handle 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 0000000..b5cc59e --- /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 0000000..2b8b113 --- /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 781b786..9213b61 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp @@ -161,6 +161,7 @@ void registerTypes() qmlRegisterExtendedType("Qt.test", 1,0, "DefaultPropertyExtendedObject"); qmlRegisterType("Qt.test", 1,0, "OverrideDefaultPropertyObject"); qmlRegisterType("Qt.test",1,0,"MyRevisionedClass"); + qmlRegisterType("Qt.test", 1,0, "MyDeleteObject"); qmlRegisterType("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 af4225c..9fbbd50 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h +++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h @@ -1229,6 +1229,26 @@ private: QList 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 ee228ec..57e4fe1 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #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" -- 2.7.4