From: Aaron Kennedy Date: Thu, 8 Mar 2012 13:25:50 +0000 (+0100) Subject: Move binding and expression classes to separate files X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=5013d53d9400f61699f8edb1dc20f06e19a26a3d;p=konrad%2Fqtdeclarative.git Move binding and expression classes to separate files Change-Id: Ia9c6996a606e140f31681ecd26d93b1b0fdedf02 Reviewed-by: Roberto Raggi --- diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 0ce7c7e..763a260 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -47,7 +47,11 @@ SOURCES += \ $$PWD/qqmlimport.cpp \ $$PWD/qqmllist.cpp \ $$PWD/qqmllocale.cpp \ - $$PWD/qlistmodelinterface.cpp + $$PWD/qlistmodelinterface.cpp \ + $$PWD/qqmlabstractexpression.cpp \ + $$PWD/qqmljavascriptexpression.cpp \ + $$PWD/qqmlabstractbinding.cpp \ + $$PWD/qqmlvaluetypeproxybinding.cpp \ HEADERS += \ $$PWD/qqmlglobal_p.h \ @@ -116,7 +120,11 @@ HEADERS += \ $$PWD/qqmlscriptstring_p.h \ $$PWD/qqmllocale_p.h \ $$PWD/qlistmodelinterface_p.h \ - $$PWD/qqmlcomponentattached_p.h + $$PWD/qqmlcomponentattached_p.h \ + $$PWD/qqmlabstractexpression_p.h \ + $$PWD/qqmljavascriptexpression_p.h \ + $$PWD/qqmlabstractbinding_p.h \ + $$PWD/qqmlvaluetypeproxybinding_p.h \ include(parser/parser.pri) include(rewriter/rewriter.pri) diff --git a/src/qml/qml/qqmlabstractbinding.cpp b/src/qml/qml/qqmlabstractbinding.cpp new file mode 100644 index 0000000..11e4ed2 --- /dev/null +++ b/src/qml/qml/qqmlabstractbinding.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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$ +** +****************************************************************************/ + +#include "qqmlabstractbinding_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +QQmlAbstractBinding::QQmlAbstractBinding() +: m_prevBinding(0), m_nextBinding(0) +{ +} + +QQmlAbstractBinding::~QQmlAbstractBinding() +{ + Q_ASSERT(m_prevBinding == 0); + Q_ASSERT(*m_mePtr == 0); +} + +/*! +Destroy the binding. Use this instead of calling delete. + +Bindings are free to implement their own memory management, so the delete operator is not +necessarily safe. The default implementation clears the binding, removes it from the object +and calls delete. +*/ +void QQmlAbstractBinding::destroy() +{ + removeFromObject(); + clear(); + + delete this; +} + +/*! +Add this binding to \a object. + +This transfers ownership of the binding to the object, marks the object's property as +being bound. + +However, it does not enable the binding itself or call update() on it. +*/ +void QQmlAbstractBinding::addToObject() +{ + Q_ASSERT(!m_prevBinding); + + QObject *obj = object(); + Q_ASSERT(obj); + + int index = propertyIndex(); + + QQmlData *data = QQmlData::get(obj, true); + + if (index & 0xFF000000) { + // Value type + + int coreIndex = index & 0xFFFFFF; + + // Find the value type proxy (if there is one) + QQmlValueTypeProxyBinding *proxy = 0; + if (data->hasBindingBit(coreIndex)) { + QQmlAbstractBinding *b = data->bindings; + while (b && b->propertyIndex() != coreIndex) + b = b->m_nextBinding; + Q_ASSERT(b && b->bindingType() == QQmlAbstractBinding::ValueTypeProxy); + proxy = static_cast(b); + } + + if (!proxy) { + proxy = new QQmlValueTypeProxyBinding(obj, coreIndex); + + Q_ASSERT(proxy->propertyIndex() == coreIndex); + Q_ASSERT(proxy->object() == obj); + + proxy->addToObject(); + } + + m_nextBinding = proxy->m_bindings; + if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding; + m_prevBinding = &proxy->m_bindings; + proxy->m_bindings = this; + + } else { + m_nextBinding = data->bindings; + if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding; + m_prevBinding = &data->bindings; + data->bindings = this; + + data->setBindingBit(obj, index); + } +} + +/*! +Remove the binding from the object. +*/ +void QQmlAbstractBinding::removeFromObject() +{ + if (m_prevBinding) { + int index = propertyIndex(); + + *m_prevBinding = m_nextBinding; + if (m_nextBinding) m_nextBinding->m_prevBinding = m_prevBinding; + m_prevBinding = 0; + m_nextBinding = 0; + + if (index & 0xFF000000) { + // Value type - we don't remove the proxy from the object. It will sit their happily + // doing nothing until it is removed by a write, a binding change or it is reused + // to hold more sub-bindings. + } else if (QObject *obj = object()) { + QQmlData *data = QQmlData::get(obj, false); + if (data) data->clearBindingBit(index); + } + } +} + +static void bindingDummyDeleter(QQmlAbstractBinding *) +{ +} + +QQmlAbstractBinding::Pointer QQmlAbstractBinding::weakPointer() +{ + if (m_mePtr.value().isNull()) + m_mePtr.value() = QSharedPointer(this, bindingDummyDeleter); + + return m_mePtr.value().toWeakRef(); +} + +void QQmlAbstractBinding::clear() +{ + if (!m_mePtr.isNull()) { + **m_mePtr = 0; + m_mePtr = 0; + } +} + +void QQmlAbstractBinding::retargetBinding(QObject *, int) +{ + qFatal("QQmlAbstractBinding::retargetBinding() called on illegal binding."); +} + +QString QQmlAbstractBinding::expression() const +{ + return QLatin1String(""); +} + +void QQmlAbstractBinding::setEnabled(bool enabled, QQmlPropertyPrivate::WriteFlags flags) +{ + if (enabled) update(flags); +} + + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h new file mode 100644 index 0000000..d226e17 --- /dev/null +++ b/src/qml/qml/qqmlabstractbinding_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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 QQMLABSTRACTBINDING_P_H +#define QQMLABSTRACTBINDING_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. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_QML_PRIVATE_EXPORT QQmlAbstractBinding +{ +public: + typedef QWeakPointer Pointer; + + QQmlAbstractBinding(); + + virtual void destroy(); + + virtual QString expression() const; + + enum Type { PropertyBinding, ValueTypeProxy }; + virtual Type bindingType() const { return PropertyBinding; } + + // Should return the encoded property index for the binding. Should return this value + // even if the binding is not enabled or added to an object. + // Encoding is: coreIndex | (valueTypeIndex << 24) + virtual int propertyIndex() const = 0; + // Should return the object for the binding. Should return this object even if the + // binding is not enabled or added to the object. + virtual QObject *object() const = 0; + + void setEnabled(bool e) { setEnabled(e, QQmlPropertyPrivate::DontRemoveBinding); } + virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags) = 0; + + void update() { update(QQmlPropertyPrivate::DontRemoveBinding); } + virtual void update(QQmlPropertyPrivate::WriteFlags) = 0; + + void addToObject(); + void removeFromObject(); + + static inline Pointer getPointer(QQmlAbstractBinding *p); + +protected: + virtual ~QQmlAbstractBinding(); + void clear(); + + // Called by QQmlPropertyPrivate to "move" a binding to a different property. + // This is only used for alias properties, and only used by QQmlBinding not + // V8 or V4 bindings. The default implementation qFatal()'s to ensure that the + // method is never called for V4 or V8 bindings. + virtual void retargetBinding(QObject *, int); +private: + Pointer weakPointer(); + + friend class QQmlData; + friend class QQmlComponentPrivate; + friend class QQmlValueTypeProxyBinding; + friend class QQmlPropertyPrivate; + friend class QQmlVME; + friend class QtSharedPointer::ExternalRefCount; + + typedef QSharedPointer SharedPointer; + // To save memory, we also store the rarely used weakPointer() instance in here + QPointerValuePair m_mePtr; + + QQmlAbstractBinding **m_prevBinding; + QQmlAbstractBinding *m_nextBinding; +}; + +QQmlAbstractBinding::Pointer +QQmlAbstractBinding::getPointer(QQmlAbstractBinding *p) +{ + return p ? p->weakPointer() : Pointer(); +} + +QT_END_NAMESPACE + +#endif // QQMLABSTRACTBINDING_P_H diff --git a/src/qml/qml/qqmlabstractexpression.cpp b/src/qml/qml/qqmlabstractexpression.cpp new file mode 100644 index 0000000..8319f7a --- /dev/null +++ b/src/qml/qml/qqmlabstractexpression.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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$ +** +****************************************************************************/ + +#include "qqmlabstractexpression_p.h" + +QT_BEGIN_NAMESPACE + +QQmlAbstractExpression::QQmlAbstractExpression() +: m_prevExpression(0), m_nextExpression(0) +{ +} + +QQmlAbstractExpression::~QQmlAbstractExpression() +{ + if (m_prevExpression) { + *m_prevExpression = m_nextExpression; + if (m_nextExpression) + m_nextExpression->m_prevExpression = m_prevExpression; + } + + if (m_context.isT2()) + m_context.asT2()->_s = 0; +} + +QQmlContextData *QQmlAbstractExpression::context() const +{ + if (m_context.isT1()) return m_context.asT1(); + else return m_context.asT2()->_c; +} + +void QQmlAbstractExpression::setContext(QQmlContextData *context) +{ + if (m_prevExpression) { + *m_prevExpression = m_nextExpression; + if (m_nextExpression) + m_nextExpression->m_prevExpression = m_prevExpression; + m_prevExpression = 0; + m_nextExpression = 0; + } + + if (m_context.isT1()) m_context = context; + else m_context.asT2()->_c = context; + + if (context) { + m_nextExpression = context->expressions; + if (m_nextExpression) + m_nextExpression->m_prevExpression = &m_nextExpression; + m_prevExpression = &context->expressions; + context->expressions = this; + } +} + +void QQmlAbstractExpression::refresh() +{ +} + +bool QQmlAbstractExpression::isValid() const +{ + return context() != 0; +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/qqmlabstractexpression_p.h b/src/qml/qml/qqmlabstractexpression_p.h new file mode 100644 index 0000000..fe2ee17 --- /dev/null +++ b/src/qml/qml/qqmlabstractexpression_p.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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 QQMLABSTRACTEXPRESSION_P_H +#define QQMLABSTRACTEXPRESSION_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. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQmlAbstractExpression +{ +public: + QQmlAbstractExpression(); + virtual ~QQmlAbstractExpression(); + + bool isValid() const; + + QQmlContextData *context() const; + void setContext(QQmlContextData *); + + virtual void refresh(); + + class DeleteWatcher { + public: + inline DeleteWatcher(QQmlAbstractExpression *); + inline ~DeleteWatcher(); + inline bool wasDeleted() const; + private: + friend class QQmlAbstractExpression; + QQmlContextData *_c; + QQmlAbstractExpression **_w; + QQmlAbstractExpression *_s; + }; + +private: + friend class QQmlContext; + friend class QQmlContextData; + friend class QQmlContextPrivate; + + QBiPointer m_context; + QQmlAbstractExpression **m_prevExpression; + QQmlAbstractExpression *m_nextExpression; +}; + +QQmlAbstractExpression::DeleteWatcher::DeleteWatcher(QQmlAbstractExpression *e) +: _c(0), _w(0), _s(e) +{ + if (e->m_context.isT1()) { + _w = &_s; + _c = e->m_context.asT1(); + e->m_context = this; + } else { + // Another watcher is already registered + _w = &e->m_context.asT2()->_s; + } +} + +QQmlAbstractExpression::DeleteWatcher::~DeleteWatcher() +{ + Q_ASSERT(*_w == 0 || (*_w == _s && _s->m_context.isT2())); + if (*_w && _s->m_context.asT2() == this) + _s->m_context = _c; +} + +bool QQmlAbstractExpression::DeleteWatcher::wasDeleted() const +{ + return *_w == 0; +} + +QT_END_NAMESPACE + +#endif // QQMLABSTRACTEXPRESSION_P_H diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index bb6eb3b..23f5abf 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -55,149 +55,6 @@ QT_BEGIN_NAMESPACE -QQmlAbstractBinding::QQmlAbstractBinding() -: m_prevBinding(0), m_nextBinding(0) -{ -} - -QQmlAbstractBinding::~QQmlAbstractBinding() -{ - Q_ASSERT(m_prevBinding == 0); - Q_ASSERT(*m_mePtr == 0); -} - -/*! -Destroy the binding. Use this instead of calling delete. - -Bindings are free to implement their own memory management, so the delete operator is not -necessarily safe. The default implementation clears the binding, removes it from the object -and calls delete. -*/ -void QQmlAbstractBinding::destroy() -{ - removeFromObject(); - clear(); - - delete this; -} - -/*! -Add this binding to \a object. - -This transfers ownership of the binding to the object, marks the object's property as -being bound. - -However, it does not enable the binding itself or call update() on it. -*/ -void QQmlAbstractBinding::addToObject() -{ - Q_ASSERT(!m_prevBinding); - - QObject *obj = object(); - Q_ASSERT(obj); - - int index = propertyIndex(); - - QQmlData *data = QQmlData::get(obj, true); - - if (index & 0xFF000000) { - // Value type - - int coreIndex = index & 0xFFFFFF; - - // Find the value type proxy (if there is one) - QQmlValueTypeProxyBinding *proxy = 0; - if (data->hasBindingBit(coreIndex)) { - QQmlAbstractBinding *b = data->bindings; - while (b && b->propertyIndex() != coreIndex) - b = b->m_nextBinding; - Q_ASSERT(b && b->bindingType() == QQmlAbstractBinding::ValueTypeProxy); - proxy = static_cast(b); - } - - if (!proxy) { - proxy = new QQmlValueTypeProxyBinding(obj, coreIndex); - - Q_ASSERT(proxy->propertyIndex() == coreIndex); - Q_ASSERT(proxy->object() == obj); - - proxy->addToObject(); - } - - m_nextBinding = proxy->m_bindings; - if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding; - m_prevBinding = &proxy->m_bindings; - proxy->m_bindings = this; - - } else { - m_nextBinding = data->bindings; - if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding; - m_prevBinding = &data->bindings; - data->bindings = this; - - data->setBindingBit(obj, index); - } -} - -/*! -Remove the binding from the object. -*/ -void QQmlAbstractBinding::removeFromObject() -{ - if (m_prevBinding) { - int index = propertyIndex(); - - *m_prevBinding = m_nextBinding; - if (m_nextBinding) m_nextBinding->m_prevBinding = m_prevBinding; - m_prevBinding = 0; - m_nextBinding = 0; - - if (index & 0xFF000000) { - // Value type - we don't remove the proxy from the object. It will sit their happily - // doing nothing until it is removed by a write, a binding change or it is reused - // to hold more sub-bindings. - } else if (QObject *obj = object()) { - QQmlData *data = QQmlData::get(obj, false); - if (data) data->clearBindingBit(index); - } - } -} - -static void bindingDummyDeleter(QQmlAbstractBinding *) -{ -} - -QQmlAbstractBinding::Pointer QQmlAbstractBinding::weakPointer() -{ - if (m_mePtr.value().isNull()) - m_mePtr.value() = QSharedPointer(this, bindingDummyDeleter); - - return m_mePtr.value().toWeakRef(); -} - -void QQmlAbstractBinding::clear() -{ - if (!m_mePtr.isNull()) { - **m_mePtr = 0; - m_mePtr = 0; - } -} - -void QQmlAbstractBinding::retargetBinding(QObject *, int) -{ - qFatal("QQmlAbstractBinding::retargetBinding() called on illegal binding."); -} - -QString QQmlAbstractBinding::expression() const -{ - return QLatin1String(""); -} - -void QQmlAbstractBinding::setEnabled(bool enabled, QQmlPropertyPrivate::WriteFlags flags) -{ - if (enabled) update(flags); -} - QQmlBinding::Identifier QQmlBinding::Invalid = -1; void QQmlBindingPrivate::refresh() @@ -457,96 +314,4 @@ void QQmlBinding::retargetBinding(QObject *t, int i) d->targetProperty = i; } -QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, int index) -: m_object(o), m_index(index), m_bindings(0) -{ -} - -QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding() -{ - while (m_bindings) { - QQmlAbstractBinding *binding = m_bindings; - binding->setEnabled(false, 0); - binding->destroy(); - } -} - -void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) -{ - if (e) { - QQmlAbstractBinding *bindings = m_bindings; - recursiveEnable(bindings, flags); - } else { - QQmlAbstractBinding *bindings = m_bindings; - recursiveDisable(bindings); - } -} - -void QQmlValueTypeProxyBinding::recursiveEnable(QQmlAbstractBinding *b, QQmlPropertyPrivate::WriteFlags flags) -{ - if (!b) - return; - - recursiveEnable(b->m_nextBinding, flags); - - if (b) - b->setEnabled(true, flags); -} - -void QQmlValueTypeProxyBinding::recursiveDisable(QQmlAbstractBinding *b) -{ - if (!b) - return; - - recursiveDisable(b->m_nextBinding); - - if (b) - b->setEnabled(false, 0); -} - -void QQmlValueTypeProxyBinding::update(QQmlPropertyPrivate::WriteFlags) -{ -} - -QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex) -{ - QQmlAbstractBinding *binding = m_bindings; - - while (binding && binding->propertyIndex() != propertyIndex) - binding = binding->m_nextBinding; - - return binding; -} - -/*! -Removes a collection of bindings, corresponding to the set bits in \a mask. -*/ -void QQmlValueTypeProxyBinding::removeBindings(quint32 mask) -{ - QQmlAbstractBinding *binding = m_bindings; - while (binding) { - if (mask & (1 << (binding->propertyIndex() >> 24))) { - QQmlAbstractBinding *remove = binding; - binding = remove->m_nextBinding; - *remove->m_prevBinding = remove->m_nextBinding; - if (remove->m_nextBinding) remove->m_nextBinding->m_prevBinding = remove->m_prevBinding; - remove->m_prevBinding = 0; - remove->m_nextBinding = 0; - remove->destroy(); - } else { - binding = binding->m_nextBinding; - } - } -} - -int QQmlValueTypeProxyBinding::propertyIndex() const -{ - return m_index; -} - -QObject *QQmlValueTypeProxyBinding::object() const -{ - return m_object; -} - QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 33823d7..785c96f 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -63,102 +63,13 @@ #include #include +#include QT_BEGIN_NAMESPACE -class Q_QML_PRIVATE_EXPORT QQmlAbstractBinding -{ -public: - typedef QWeakPointer Pointer; - - QQmlAbstractBinding(); - - virtual void destroy(); - - virtual QString expression() const; - - enum Type { PropertyBinding, ValueTypeProxy }; - virtual Type bindingType() const { return PropertyBinding; } - - // Should return the encoded property index for the binding. Should return this value - // even if the binding is not enabled or added to an object. - // Encoding is: coreIndex | (valueTypeIndex << 24) - virtual int propertyIndex() const = 0; - // Should return the object for the binding. Should return this object even if the - // binding is not enabled or added to the object. - virtual QObject *object() const = 0; - - void setEnabled(bool e) { setEnabled(e, QQmlPropertyPrivate::DontRemoveBinding); } - virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags) = 0; - - void update() { update(QQmlPropertyPrivate::DontRemoveBinding); } - virtual void update(QQmlPropertyPrivate::WriteFlags) = 0; - - void addToObject(); - void removeFromObject(); - - static inline Pointer getPointer(QQmlAbstractBinding *p); - -protected: - virtual ~QQmlAbstractBinding(); - void clear(); - - // Called by QQmlPropertyPrivate to "move" a binding to a different property. - // This is only used for alias properties, and only used by QQmlBinding not - // V8 or V4 bindings. The default implementation qFatal()'s to ensure that the - // method is never called for V4 or V8 bindings. - virtual void retargetBinding(QObject *, int); -private: - Pointer weakPointer(); - - friend class QQmlData; - friend class QQmlComponentPrivate; - friend class QQmlValueTypeProxyBinding; - friend class QQmlPropertyPrivate; - friend class QQmlVME; - friend class QtSharedPointer::ExternalRefCount; - - typedef QSharedPointer SharedPointer; - // To save memory, we also store the rarely used weakPointer() instance in here - QPointerValuePair m_mePtr; - - QQmlAbstractBinding **m_prevBinding; - QQmlAbstractBinding *m_nextBinding; -}; - -class QQmlValueTypeProxyBinding : public QQmlAbstractBinding -{ -public: - QQmlValueTypeProxyBinding(QObject *o, int coreIndex); - - virtual Type bindingType() const { return ValueTypeProxy; } - - virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags); - virtual void update(QQmlPropertyPrivate::WriteFlags); - virtual int propertyIndex() const; - virtual QObject *object() const; - - QQmlAbstractBinding *binding(int propertyIndex); - - void removeBindings(quint32 mask); - -protected: - ~QQmlValueTypeProxyBinding(); - -private: - void recursiveEnable(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags); - void recursiveDisable(QQmlAbstractBinding *); - - friend class QQmlAbstractBinding; - QObject *m_object; - int m_index; - QQmlAbstractBinding *m_bindings; -}; - class QQmlContext; class QQmlBindingPrivate; -class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlExpression, - public QQmlAbstractBinding +class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlExpression, public QQmlAbstractBinding { Q_OBJECT public: @@ -206,12 +117,6 @@ private: Q_DECLARE_OPERATORS_FOR_FLAGS(QQmlBinding::EvaluateFlags) -QQmlAbstractBinding::Pointer -QQmlAbstractBinding::getPointer(QQmlAbstractBinding *p) -{ - return p ? p->weakPointer() : Pointer(); -} - QT_END_NAMESPACE Q_DECLARE_METATYPE(QQmlBinding*) diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index ecdbf21..cefde1d 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -52,32 +52,6 @@ QT_BEGIN_NAMESPACE -bool QQmlDelayedError::addError(QQmlEnginePrivate *e) -{ - if (!e) return false; - - if (e->inProgressCreations == 0) return false; // Not in construction - - if (prevError) return true; // Already in error chain - - prevError = &e->erroredBindings; - nextError = e->erroredBindings; - e->erroredBindings = this; - if (nextError) nextError->prevError = &nextError; - - return true; -} - -QQmlJavaScriptExpression::QQmlJavaScriptExpression(VTable *v) -: m_vtable(v) -{ -} - -QQmlJavaScriptExpression::~QQmlJavaScriptExpression() -{ - clearGuards(); -} - static QQmlJavaScriptExpression::VTable QQmlExpressionPrivate_jsvtable = { QQmlExpressionPrivate::expressionIdentifier, QQmlExpressionPrivate::expressionChanged @@ -514,192 +488,6 @@ void QQmlExpressionPrivate::exceptionToError(v8::Handle message, error.setDescription(qDescription); } -void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v) -{ - activeGuards.setFlagValue(v); - if (!v) clearGuards(); -} - -void QQmlJavaScriptExpression::resetNotifyOnValueChanged() -{ - clearGuards(); -} - -v8::Local -QQmlJavaScriptExpression::evaluate(QQmlContextData *context, - v8::Handle function, bool *isUndefined) -{ - Q_ASSERT(context && context->engine); - - if (function.IsEmpty() || function->IsUndefined()) { - if (isUndefined) *isUndefined = true; - return v8::Local(); - } - - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); - - Q_ASSERT(notifyOnValueChanged() || activeGuards.isEmpty()); - GuardCapture capture(context->engine, this); - - QQmlEnginePrivate::PropertyCapture *lastPropertyCapture = ep->propertyCapture; - ep->propertyCapture = notifyOnValueChanged()?&capture:0; - - - if (notifyOnValueChanged()) - capture.guards.copyAndClearPrepend(activeGuards); - - QQmlContextData *lastSharedContext = 0; - QObject *lastSharedScope = 0; - - bool sharedContext = useSharedContext(); - - // All code that follows must check with watcher before it accesses data members - // incase we have been deleted. - DeleteWatcher watcher(this); - - if (sharedContext) { - lastSharedContext = ep->sharedContext; - lastSharedScope = ep->sharedScope; - ep->sharedContext = context; - ep->sharedScope = scopeObject(); - } - - v8::Local result; - { - v8::TryCatch try_catch; - v8::Handle This = ep->v8engine()->global(); - if (scopeObject() && requiresThisObject()) { - v8::Handle value = ep->v8engine()->newQObject(scopeObject()); - if (value->IsObject()) This = v8::Handle::Cast(value); - } - - result = function->Call(This, 0, 0); - - if (isUndefined) - *isUndefined = try_catch.HasCaught() || result->IsUndefined(); - - if (watcher.wasDeleted()) { - } else if (try_catch.HasCaught()) { - v8::Context::Scope scope(ep->v8engine()->context()); - v8::Local message = try_catch.Message(); - if (!message.IsEmpty()) { - QQmlExpressionPrivate::exceptionToError(message, delayedError()->error); - } else { - if (hasDelayedError()) delayedError()->error = QQmlError(); - } - } else { - if (hasDelayedError()) delayedError()->error = QQmlError(); - } - } - - if (sharedContext) { - ep->sharedContext = lastSharedContext; - ep->sharedScope = lastSharedScope; - } - - 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; - } - - while (Guard *g = capture.guards.takeFirst()) - g->Delete(); - - ep->propertyCapture = lastPropertyCapture; - - return result; -} - -void QQmlJavaScriptExpression::GuardCapture::captureProperty(QQmlNotifier *n) -{ - if (expression) { - - // Try and find a matching guard - while (!guards.isEmpty() && !guards.first()->isConnected(n)) - guards.takeFirst()->Delete(); - - Guard *g = 0; - if (!guards.isEmpty()) { - g = guards.takeFirst(); - g->cancelNotify(); - Q_ASSERT(g->isConnected(n)); - } else { - g = Guard::New(expression, engine); - g->connect(n); - } - - expression->activeGuards.prepend(g); - } -} - -void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, int n) -{ - if (expression) { - if (n == -1) { - if (!errorString) { - errorString = new QStringList; - QString preamble = QLatin1String("QQmlExpression: Expression ") + - expression->m_vtable->expressionIdentifier(expression) + - QLatin1String(" depends on non-NOTIFYable properties:"); - errorString->append(preamble); - } - - 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 { - - // 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, engine); - g->connect(o, n); - } - - expression->activeGuards.prepend(g); - } - } -} - -void QQmlJavaScriptExpression::clearError() -{ - if (m_vtable.hasValue()) { - m_vtable.value().error = QQmlError(); - m_vtable.value().removeError(); - } -} - -QQmlError QQmlJavaScriptExpression::error() const -{ - if (m_vtable.hasValue()) return m_vtable.constValue()->error; - else return QQmlError(); -} - -QQmlDelayedError *QQmlJavaScriptExpression::delayedError() -{ - return &m_vtable.value(); -} - -void QQmlJavaScriptExpression::clearGuards() -{ - while (Guard *g = activeGuards.takeFirst()) - g->Delete(); -} - // Must be called with a valid handle scope v8::Local QQmlExpressionPrivate::v8value(QObject *secondaryScope, bool *isUndefined) { @@ -923,60 +711,6 @@ QString QQmlExpressionPrivate::expressionIdentifier(QQmlJavaScriptExpression *e) return QLatin1String("\"") + This->expression + QLatin1String("\""); } -QQmlAbstractExpression::QQmlAbstractExpression() -: m_prevExpression(0), m_nextExpression(0) -{ -} - -QQmlAbstractExpression::~QQmlAbstractExpression() -{ - if (m_prevExpression) { - *m_prevExpression = m_nextExpression; - if (m_nextExpression) - m_nextExpression->m_prevExpression = m_prevExpression; - } - - if (m_context.isT2()) - m_context.asT2()->_s = 0; -} - -QQmlContextData *QQmlAbstractExpression::context() const -{ - if (m_context.isT1()) return m_context.asT1(); - else return m_context.asT2()->_c; -} - -void QQmlAbstractExpression::setContext(QQmlContextData *context) -{ - if (m_prevExpression) { - *m_prevExpression = m_nextExpression; - if (m_nextExpression) - m_nextExpression->m_prevExpression = m_prevExpression; - m_prevExpression = 0; - m_nextExpression = 0; - } - - if (m_context.isT1()) m_context = context; - else m_context.asT2()->_c = context; - - if (context) { - m_nextExpression = context->expressions; - if (m_nextExpression) - m_nextExpression->m_prevExpression = &m_nextExpression; - m_prevExpression = &context->expressions; - context->expressions = this; - } -} - -void QQmlAbstractExpression::refresh() -{ -} - -bool QQmlAbstractExpression::isValid() const -{ - return context() != 0; -} - QT_END_NAMESPACE #include diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h index deca29a..3b335ba 100644 --- a/src/qml/qml/qqmlexpression_p.h +++ b/src/qml/qml/qqmlexpression_p.h @@ -56,161 +56,22 @@ #include "qqmlexpression.h" #include +#include +#include #include #include #include -#include #include -#include +#include +#include QT_BEGIN_NAMESPACE -class QQmlAbstractExpression -{ -public: - QQmlAbstractExpression(); - virtual ~QQmlAbstractExpression(); - - bool isValid() const; - - QQmlContextData *context() const; - void setContext(QQmlContextData *); - - virtual void refresh(); - - class DeleteWatcher { - public: - inline DeleteWatcher(QQmlAbstractExpression *); - inline ~DeleteWatcher(); - inline bool wasDeleted() const; - private: - friend class QQmlAbstractExpression; - QQmlContextData *_c; - QQmlAbstractExpression **_w; - QQmlAbstractExpression *_s; - }; - -private: - friend class QQmlContext; - friend class QQmlContextData; - friend class QQmlContextPrivate; - - QBiPointer m_context; - QQmlAbstractExpression **m_prevExpression; - QQmlAbstractExpression *m_nextExpression; -}; - -class QQmlDelayedError -{ -public: - inline QQmlDelayedError() : nextError(0), prevError(0) {} - inline ~QQmlDelayedError() { removeError(); } - - QQmlError error; - - bool addError(QQmlEnginePrivate *); - - inline void removeError() { - if (!prevError) return; - if (nextError) nextError->prevError = prevError; - *prevError = nextError; - nextError = 0; - prevError = 0; - } - -private: - QQmlDelayedError *nextError; - QQmlDelayedError **prevError; -}; - -class QQmlJavaScriptExpression -{ -public: - // Although this looks crazy, we implement our own "vtable" here, rather than relying on - // C++ virtuals, to save memory. By doing it ourselves, we can overload the storage - // location that is use for the vtable to also store the rarely used delayed error. - // If we use C++ virtuals, we can't do this and it consts us an extra sizeof(void *) in - // memory for every expression. - struct VTable { - QString (*expressionIdentifier)(QQmlJavaScriptExpression *); - void (*expressionChanged)(QQmlJavaScriptExpression *); - }; - - QQmlJavaScriptExpression(VTable *vtable); - - v8::Local evaluate(QQmlContextData *, v8::Handle, - bool *isUndefined); - - inline bool requiresThisObject() const; - inline void setRequiresThisObject(bool v); - inline bool useSharedContext() const; - inline void setUseSharedContext(bool v); - inline bool notifyOnValueChanged() const; - - void setNotifyOnValueChanged(bool v); - void resetNotifyOnValueChanged(); - - inline QObject *scopeObject() const; - inline void setScopeObject(QObject *v); - - class DeleteWatcher { - public: - inline DeleteWatcher(QQmlJavaScriptExpression *); - inline ~DeleteWatcher(); - inline bool wasDeleted() const; - private: - friend class QQmlJavaScriptExpression; - QObject *_c; - QQmlJavaScriptExpression **_w; - QQmlJavaScriptExpression *_s; - }; - - inline bool hasError() const; - inline bool hasDelayedError() const; - QQmlError error() const; - void clearError(); - QQmlDelayedError *delayedError(); - -protected: - ~QQmlJavaScriptExpression(); - -private: - typedef QQmlJavaScriptExpressionGuard Guard; - friend class QQmlJavaScriptExpressionGuard; - - struct GuardCapture : public QQmlEnginePrivate::PropertyCapture { - GuardCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e) - : engine(engine), expression(e), errorString(0) { } - - ~GuardCapture() { - Q_ASSERT(guards.isEmpty()); - Q_ASSERT(errorString == 0); - } - - virtual void captureProperty(QQmlNotifier *); - virtual void captureProperty(QObject *, int, int); - - QQmlEngine *engine; - QQmlJavaScriptExpression *expression; - QFieldList guards; - QStringList *errorString; - }; - - QPointerValuePair m_vtable; - - // We store some flag bits in the following flag pointers. - // m_scopeObject:flag1 - requiresThisObject - // activeGuards:flag1 - notifyOnValueChanged - // activeGuards:flag2 - useSharedContext - QBiPointer m_scopeObject; - QForwardFieldList activeGuards; - - void clearGuards(); -}; - class QQmlExpression; class QString; -class Q_QML_PRIVATE_EXPORT QQmlExpressionPrivate : public QObjectPrivate, public QQmlJavaScriptExpression, public QQmlAbstractExpression +class Q_QML_PRIVATE_EXPORT QQmlExpressionPrivate : public QObjectPrivate, + public QQmlJavaScriptExpression, + public QQmlAbstractExpression { Q_DECLARE_PUBLIC(QQmlExpression) public: @@ -267,103 +128,6 @@ public: QQmlRefCount *dataRef; }; -QQmlAbstractExpression::DeleteWatcher::DeleteWatcher(QQmlAbstractExpression *e) -: _c(0), _w(0), _s(e) -{ - if (e->m_context.isT1()) { - _w = &_s; - _c = e->m_context.asT1(); - e->m_context = this; - } else { - // Another watcher is already registered - _w = &e->m_context.asT2()->_s; - } -} - -QQmlAbstractExpression::DeleteWatcher::~DeleteWatcher() -{ - Q_ASSERT(*_w == 0 || (*_w == _s && _s->m_context.isT2())); - if (*_w && _s->m_context.asT2() == this) - _s->m_context = _c; -} - -bool QQmlAbstractExpression::DeleteWatcher::wasDeleted() const -{ - return *_w == 0; -} - -QQmlJavaScriptExpression::DeleteWatcher::DeleteWatcher(QQmlJavaScriptExpression *e) -: _c(0), _w(0), _s(e) -{ - if (e->m_scopeObject.isT1()) { - _w = &_s; - _c = e->m_scopeObject.asT1(); - e->m_scopeObject = this; - } else { - // Another watcher is already registered - _w = &e->m_scopeObject.asT2()->_s; - } -} - -QQmlJavaScriptExpression::DeleteWatcher::~DeleteWatcher() -{ - Q_ASSERT(*_w == 0 || (*_w == _s && _s->m_scopeObject.isT2())); - if (*_w && _s->m_scopeObject.asT2() == this) - _s->m_scopeObject = _c; -} - -bool QQmlJavaScriptExpression::DeleteWatcher::wasDeleted() const -{ - return *_w == 0; -} - -bool QQmlJavaScriptExpression::requiresThisObject() const -{ - return m_scopeObject.flag(); -} - -void QQmlJavaScriptExpression::setRequiresThisObject(bool v) -{ - m_scopeObject.setFlagValue(v); -} - -bool QQmlJavaScriptExpression::useSharedContext() const -{ - return activeGuards.flag2(); -} - -void QQmlJavaScriptExpression::setUseSharedContext(bool v) -{ - activeGuards.setFlag2Value(v); -} - -bool QQmlJavaScriptExpression::notifyOnValueChanged() const -{ - return activeGuards.flag(); -} - -QObject *QQmlJavaScriptExpression::scopeObject() const -{ - if (m_scopeObject.isT1()) return m_scopeObject.asT1(); - else return m_scopeObject.asT2()->_c; -} - -void QQmlJavaScriptExpression::setScopeObject(QObject *v) -{ - if (m_scopeObject.isT1()) m_scopeObject = v; - else m_scopeObject.asT2()->_c = v; -} - -bool QQmlJavaScriptExpression::hasError() const -{ - return m_vtable.hasValue() && m_vtable.constValue()->error.isValid(); -} - -bool QQmlJavaScriptExpression::hasDelayedError() const -{ - return m_vtable.hasValue(); -} - QQmlExpressionPrivate *QQmlExpressionPrivate::get(QQmlExpression *expr) { return static_cast(QObjectPrivate::get(expr)); @@ -374,32 +138,6 @@ QQmlExpression *QQmlExpressionPrivate::get(QQmlExpressionPrivate *expr) return expr->q_func(); } -QQmlJavaScriptExpressionGuard::QQmlJavaScriptExpressionGuard(QQmlJavaScriptExpression *e) -: expression(e), next(0) -{ - callback = &endpointCallback; -} - -void QQmlJavaScriptExpressionGuard::endpointCallback(QQmlNotifierEndpoint *e) -{ - QQmlJavaScriptExpression *expression = - static_cast(e)->expression; - - expression->m_vtable->expressionChanged(expression); -} - -QQmlJavaScriptExpressionGuard * -QQmlJavaScriptExpressionGuard::New(QQmlJavaScriptExpression *e, - QQmlEngine *engine) -{ - Q_ASSERT(e); - return QQmlEnginePrivate::get(engine)->jsExpressionGuardPool.New(e); -} - -void QQmlJavaScriptExpressionGuard::Delete() -{ - QRecyclePool::Delete(this); -} QT_END_NAMESPACE diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp new file mode 100644 index 0000000..4186b5b --- /dev/null +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -0,0 +1,260 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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$ +** +****************************************************************************/ + +#include "qqmljavascriptexpression_p.h" + +#include + +QT_BEGIN_NAMESPACE + +bool QQmlDelayedError::addError(QQmlEnginePrivate *e) +{ + if (!e) return false; + + if (e->inProgressCreations == 0) return false; // Not in construction + + if (prevError) return true; // Already in error chain + + prevError = &e->erroredBindings; + nextError = e->erroredBindings; + e->erroredBindings = this; + if (nextError) nextError->prevError = &nextError; + + return true; +} + +QQmlJavaScriptExpression::QQmlJavaScriptExpression(VTable *v) +: m_vtable(v) +{ +} + +QQmlJavaScriptExpression::~QQmlJavaScriptExpression() +{ + clearGuards(); +} + +void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v) +{ + activeGuards.setFlagValue(v); + if (!v) clearGuards(); +} + +void QQmlJavaScriptExpression::resetNotifyOnValueChanged() +{ + clearGuards(); +} + +v8::Local +QQmlJavaScriptExpression::evaluate(QQmlContextData *context, + v8::Handle function, bool *isUndefined) +{ + Q_ASSERT(context && context->engine); + + if (function.IsEmpty() || function->IsUndefined()) { + if (isUndefined) *isUndefined = true; + return v8::Local(); + } + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); + + Q_ASSERT(notifyOnValueChanged() || activeGuards.isEmpty()); + GuardCapture capture(context->engine, this); + + QQmlEnginePrivate::PropertyCapture *lastPropertyCapture = ep->propertyCapture; + ep->propertyCapture = notifyOnValueChanged()?&capture:0; + + + if (notifyOnValueChanged()) + capture.guards.copyAndClearPrepend(activeGuards); + + QQmlContextData *lastSharedContext = 0; + QObject *lastSharedScope = 0; + + bool sharedContext = useSharedContext(); + + // All code that follows must check with watcher before it accesses data members + // incase we have been deleted. + DeleteWatcher watcher(this); + + if (sharedContext) { + lastSharedContext = ep->sharedContext; + lastSharedScope = ep->sharedScope; + ep->sharedContext = context; + ep->sharedScope = scopeObject(); + } + + v8::Local result; + { + v8::TryCatch try_catch; + v8::Handle This = ep->v8engine()->global(); + if (scopeObject() && requiresThisObject()) { + v8::Handle value = ep->v8engine()->newQObject(scopeObject()); + if (value->IsObject()) This = v8::Handle::Cast(value); + } + + result = function->Call(This, 0, 0); + + if (isUndefined) + *isUndefined = try_catch.HasCaught() || result->IsUndefined(); + + if (watcher.wasDeleted()) { + } else if (try_catch.HasCaught()) { + v8::Context::Scope scope(ep->v8engine()->context()); + v8::Local message = try_catch.Message(); + if (!message.IsEmpty()) { + QQmlExpressionPrivate::exceptionToError(message, delayedError()->error); + } else { + if (hasDelayedError()) delayedError()->error = QQmlError(); + } + } else { + if (hasDelayedError()) delayedError()->error = QQmlError(); + } + } + + if (sharedContext) { + ep->sharedContext = lastSharedContext; + ep->sharedScope = lastSharedScope; + } + + 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; + } + + while (Guard *g = capture.guards.takeFirst()) + g->Delete(); + + ep->propertyCapture = lastPropertyCapture; + + return result; +} + +void QQmlJavaScriptExpression::GuardCapture::captureProperty(QQmlNotifier *n) +{ + if (expression) { + + // Try and find a matching guard + while (!guards.isEmpty() && !guards.first()->isConnected(n)) + guards.takeFirst()->Delete(); + + Guard *g = 0; + if (!guards.isEmpty()) { + g = guards.takeFirst(); + g->cancelNotify(); + Q_ASSERT(g->isConnected(n)); + } else { + g = Guard::New(expression, engine); + g->connect(n); + } + + expression->activeGuards.prepend(g); + } +} + +void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c, int n) +{ + if (expression) { + if (n == -1) { + if (!errorString) { + errorString = new QStringList; + QString preamble = QLatin1String("QQmlExpression: Expression ") + + expression->m_vtable->expressionIdentifier(expression) + + QLatin1String(" depends on non-NOTIFYable properties:"); + errorString->append(preamble); + } + + 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 { + + // 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, engine); + g->connect(o, n); + } + + expression->activeGuards.prepend(g); + } + } +} + +void QQmlJavaScriptExpression::clearError() +{ + if (m_vtable.hasValue()) { + m_vtable.value().error = QQmlError(); + m_vtable.value().removeError(); + } +} + +QQmlError QQmlJavaScriptExpression::error() const +{ + if (m_vtable.hasValue()) return m_vtable.constValue()->error; + else return QQmlError(); +} + +QQmlDelayedError *QQmlJavaScriptExpression::delayedError() +{ + return &m_vtable.value(); +} + +void QQmlJavaScriptExpression::clearGuards() +{ + while (Guard *g = activeGuards.takeFirst()) + g->Delete(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h new file mode 100644 index 0000000..1da1e12 --- /dev/null +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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 QQMLJAVASCRIPTEXPRESSION_P_H +#define QQMLJAVASCRIPTEXPRESSION_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. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQmlDelayedError +{ +public: + inline QQmlDelayedError() : nextError(0), prevError(0) {} + inline ~QQmlDelayedError() { removeError(); } + + QQmlError error; + + bool addError(QQmlEnginePrivate *); + + inline void removeError() { + if (!prevError) return; + if (nextError) nextError->prevError = prevError; + *prevError = nextError; + nextError = 0; + prevError = 0; + } + +private: + QQmlDelayedError *nextError; + QQmlDelayedError **prevError; +}; + +class QQmlJavaScriptExpression +{ +public: + // Although this looks crazy, we implement our own "vtable" here, rather than relying on + // C++ virtuals, to save memory. By doing it ourselves, we can overload the storage + // location that is use for the vtable to also store the rarely used delayed error. + // If we use C++ virtuals, we can't do this and it consts us an extra sizeof(void *) in + // memory for every expression. + struct VTable { + QString (*expressionIdentifier)(QQmlJavaScriptExpression *); + void (*expressionChanged)(QQmlJavaScriptExpression *); + }; + + QQmlJavaScriptExpression(VTable *vtable); + + v8::Local evaluate(QQmlContextData *, v8::Handle, + bool *isUndefined); + + inline bool requiresThisObject() const; + inline void setRequiresThisObject(bool v); + inline bool useSharedContext() const; + inline void setUseSharedContext(bool v); + inline bool notifyOnValueChanged() const; + + void setNotifyOnValueChanged(bool v); + void resetNotifyOnValueChanged(); + + inline QObject *scopeObject() const; + inline void setScopeObject(QObject *v); + + class DeleteWatcher { + public: + inline DeleteWatcher(QQmlJavaScriptExpression *); + inline ~DeleteWatcher(); + inline bool wasDeleted() const; + private: + friend class QQmlJavaScriptExpression; + QObject *_c; + QQmlJavaScriptExpression **_w; + QQmlJavaScriptExpression *_s; + }; + + inline bool hasError() const; + inline bool hasDelayedError() const; + QQmlError error() const; + void clearError(); + QQmlDelayedError *delayedError(); + +protected: + ~QQmlJavaScriptExpression(); + +private: + typedef QQmlJavaScriptExpressionGuard Guard; + friend class QQmlJavaScriptExpressionGuard; + + struct GuardCapture : public QQmlEnginePrivate::PropertyCapture { + GuardCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e) + : engine(engine), expression(e), errorString(0) { } + + ~GuardCapture() { + Q_ASSERT(guards.isEmpty()); + Q_ASSERT(errorString == 0); + } + + virtual void captureProperty(QQmlNotifier *); + virtual void captureProperty(QObject *, int, int); + + QQmlEngine *engine; + QQmlJavaScriptExpression *expression; + QFieldList guards; + QStringList *errorString; + }; + + QPointerValuePair m_vtable; + + // We store some flag bits in the following flag pointers. + // m_scopeObject:flag1 - requiresThisObject + // activeGuards:flag1 - notifyOnValueChanged + // activeGuards:flag2 - useSharedContext + QBiPointer m_scopeObject; + QForwardFieldList activeGuards; + + void clearGuards(); +}; + +QQmlJavaScriptExpression::DeleteWatcher::DeleteWatcher(QQmlJavaScriptExpression *e) +: _c(0), _w(0), _s(e) +{ + if (e->m_scopeObject.isT1()) { + _w = &_s; + _c = e->m_scopeObject.asT1(); + e->m_scopeObject = this; + } else { + // Another watcher is already registered + _w = &e->m_scopeObject.asT2()->_s; + } +} + +QQmlJavaScriptExpression::DeleteWatcher::~DeleteWatcher() +{ + Q_ASSERT(*_w == 0 || (*_w == _s && _s->m_scopeObject.isT2())); + if (*_w && _s->m_scopeObject.asT2() == this) + _s->m_scopeObject = _c; +} + +bool QQmlJavaScriptExpression::DeleteWatcher::wasDeleted() const +{ + return *_w == 0; +} + +bool QQmlJavaScriptExpression::requiresThisObject() const +{ + return m_scopeObject.flag(); +} + +void QQmlJavaScriptExpression::setRequiresThisObject(bool v) +{ + m_scopeObject.setFlagValue(v); +} + +bool QQmlJavaScriptExpression::useSharedContext() const +{ + return activeGuards.flag2(); +} + +void QQmlJavaScriptExpression::setUseSharedContext(bool v) +{ + activeGuards.setFlag2Value(v); +} + +bool QQmlJavaScriptExpression::notifyOnValueChanged() const +{ + return activeGuards.flag(); +} + +QObject *QQmlJavaScriptExpression::scopeObject() const +{ + if (m_scopeObject.isT1()) return m_scopeObject.asT1(); + else return m_scopeObject.asT2()->_c; +} + +void QQmlJavaScriptExpression::setScopeObject(QObject *v) +{ + if (m_scopeObject.isT1()) m_scopeObject = v; + else m_scopeObject.asT2()->_c = v; +} + +bool QQmlJavaScriptExpression::hasError() const +{ + return m_vtable.hasValue() && m_vtable.constValue()->error.isValid(); +} + +bool QQmlJavaScriptExpression::hasDelayedError() const +{ + return m_vtable.hasValue(); +} + +QQmlJavaScriptExpressionGuard::QQmlJavaScriptExpressionGuard(QQmlJavaScriptExpression *e) +: expression(e), next(0) +{ + callback = &endpointCallback; +} + +void QQmlJavaScriptExpressionGuard::endpointCallback(QQmlNotifierEndpoint *e) +{ + QQmlJavaScriptExpression *expression = + static_cast(e)->expression; + + expression->m_vtable->expressionChanged(expression); +} + +QQmlJavaScriptExpressionGuard * +QQmlJavaScriptExpressionGuard::New(QQmlJavaScriptExpression *e, + QQmlEngine *engine) +{ + Q_ASSERT(e); + return QQmlEnginePrivate::get(engine)->jsExpressionGuardPool.New(e); +} + +void QQmlJavaScriptExpressionGuard::Delete() +{ + QRecyclePool::Delete(this); +} + + +QT_END_NAMESPACE + +#endif // QQMLJAVASCRIPTEXPRESSION_P_H diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 6321592..8d5d83e 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -55,6 +55,7 @@ #include "qqmlcompiler_p.h" #include "qqmlvmemetaobject_p.h" #include "qqmlexpression_p.h" +#include "qqmlvaluetypeproxybinding_p.h" #include #include diff --git a/src/qml/qml/qqmlvaluetypeproxybinding.cpp b/src/qml/qml/qqmlvaluetypeproxybinding.cpp new file mode 100644 index 0000000..2cc15a5 --- /dev/null +++ b/src/qml/qml/qqmlvaluetypeproxybinding.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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$ +** +****************************************************************************/ + +#include "qqmlvaluetypeproxybinding_p.h" + +QT_BEGIN_NAMESPACE + +QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, int index) +: m_object(o), m_index(index), m_bindings(0) +{ +} + +QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding() +{ + while (m_bindings) { + QQmlAbstractBinding *binding = m_bindings; + binding->setEnabled(false, 0); + binding->destroy(); + } +} + +void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) +{ + if (e) { + QQmlAbstractBinding *bindings = m_bindings; + recursiveEnable(bindings, flags); + } else { + QQmlAbstractBinding *bindings = m_bindings; + recursiveDisable(bindings); + } +} + +void QQmlValueTypeProxyBinding::recursiveEnable(QQmlAbstractBinding *b, QQmlPropertyPrivate::WriteFlags flags) +{ + if (!b) + return; + + recursiveEnable(b->m_nextBinding, flags); + + if (b) + b->setEnabled(true, flags); +} + +void QQmlValueTypeProxyBinding::recursiveDisable(QQmlAbstractBinding *b) +{ + if (!b) + return; + + recursiveDisable(b->m_nextBinding); + + if (b) + b->setEnabled(false, 0); +} + +void QQmlValueTypeProxyBinding::update(QQmlPropertyPrivate::WriteFlags) +{ +} + +QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex) +{ + QQmlAbstractBinding *binding = m_bindings; + + while (binding && binding->propertyIndex() != propertyIndex) + binding = binding->m_nextBinding; + + return binding; +} + +/*! +Removes a collection of bindings, corresponding to the set bits in \a mask. +*/ +void QQmlValueTypeProxyBinding::removeBindings(quint32 mask) +{ + QQmlAbstractBinding *binding = m_bindings; + while (binding) { + if (mask & (1 << (binding->propertyIndex() >> 24))) { + QQmlAbstractBinding *remove = binding; + binding = remove->m_nextBinding; + *remove->m_prevBinding = remove->m_nextBinding; + if (remove->m_nextBinding) remove->m_nextBinding->m_prevBinding = remove->m_prevBinding; + remove->m_prevBinding = 0; + remove->m_nextBinding = 0; + remove->destroy(); + } else { + binding = binding->m_nextBinding; + } + } +} + +int QQmlValueTypeProxyBinding::propertyIndex() const +{ + return m_index; +} + +QObject *QQmlValueTypeProxyBinding::object() const +{ + return m_object; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlvaluetypeproxybinding_p.h b/src/qml/qml/qqmlvaluetypeproxybinding_p.h new file mode 100644 index 0000000..0135d57 --- /dev/null +++ b/src/qml/qml/qqmlvaluetypeproxybinding_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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 QQMLVALUETYPEPROXYBINDING_P_H +#define QQMLVALUETYPEPROXYBINDING_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. +// + +#include + +QT_BEGIN_NAMESPACE + +class QQmlValueTypeProxyBinding : public QQmlAbstractBinding +{ +public: + QQmlValueTypeProxyBinding(QObject *o, int coreIndex); + + virtual Type bindingType() const { return ValueTypeProxy; } + + virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags); + virtual void update(QQmlPropertyPrivate::WriteFlags); + virtual int propertyIndex() const; + virtual QObject *object() const; + + QQmlAbstractBinding *binding(int propertyIndex); + + void removeBindings(quint32 mask); + +protected: + ~QQmlValueTypeProxyBinding(); + +private: + void recursiveEnable(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags); + void recursiveDisable(QQmlAbstractBinding *); + + friend class QQmlAbstractBinding; + QObject *m_object; + int m_index; + QQmlAbstractBinding *m_bindings; +}; + +QT_END_NAMESPACE + +#endif // QQMLVALUETYPEPROXYBINDING_P_H diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp index 2b66e00..99979ab 100644 --- a/src/qml/qml/qqmlvme.cpp +++ b/src/qml/qml/qqmlvme.cpp @@ -66,6 +66,7 @@ #include "qqmlscriptstring.h" #include "qqmlscriptstring_p.h" #include "qqmlpropertyvalueinterceptor_p.h" +#include "qqmlvaluetypeproxybinding_p.h" #include #include