diff options
Diffstat (limited to 'src/qml/qml/qqmlbinding.cpp')
-rw-r--r-- | src/qml/qml/qqmlbinding.cpp | 478 |
1 files changed, 136 insertions, 342 deletions
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index bb6eb3b723..f43822ed0e 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -40,7 +40,6 @@ ****************************************************************************/ #include "qqmlbinding_p.h" -#include "qqmlbinding_p_p.h" #include "qqml.h" #include "qqmlcontext.h" @@ -49,175 +48,19 @@ #include "qqmldata_p.h" #include <private/qqmlprofilerservice_p.h> #include <private/qqmltrace_p.h> +#include <private/qqmlexpression_p.h> +#include <private/qqmlrewrite_p.h> #include <QVariant> #include <QtCore/qdebug.h> 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<QQmlValueTypeProxyBinding *>(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<QQmlAbstractBinding>(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("<Unknown>"); -} - -void QQmlAbstractBinding::setEnabled(bool enabled, QQmlPropertyPrivate::WriteFlags flags) -{ - if (enabled) update(flags); -} - QQmlBinding::Identifier QQmlBinding::Invalid = -1; -void QQmlBindingPrivate::refresh() -{ - Q_Q(QQmlBinding); - q->update(); -} - -QQmlBindingPrivate::QQmlBindingPrivate() -: updating(false), enabled(false), target(), targetProperty(0) -{ -} - -QQmlBindingPrivate::~QQmlBindingPrivate() -{ -} - QQmlBinding * QQmlBinding::createBinding(Identifier id, QObject *obj, QQmlContext *ctxt, - const QString &url, int lineNumber, QObject *parent) + const QString &url, int lineNumber) { if (id < 0) return 0; @@ -231,7 +74,8 @@ QQmlBinding::createBinding(Identifier id, QObject *obj, QQmlContext *ctxt, typeData = engine->typeLoader.get(ctxtdata->url); cdata = typeData->compiledData(); } - QQmlBinding *rv = cdata ? new QQmlBinding(cdata->primitives.at(id), true, obj, ctxtdata, url, lineNumber, 0, parent) : 0; + QQmlBinding *rv = cdata ? new QQmlBinding(cdata->primitives.at(id), true, obj, ctxtdata, + url, lineNumber, 0) : 0; if (cdata) cdata->release(); if (typeData) @@ -239,30 +83,62 @@ QQmlBinding::createBinding(Identifier id, QObject *obj, QQmlContext *ctxt, return rv; } -QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt, - QObject *parent) -: QQmlExpression(QQmlContextData::get(ctxt), obj, str, *new QQmlBindingPrivate) +static QQmlJavaScriptExpression::VTable QQmlBinding_jsvtable = { + QQmlBinding::expressionIdentifier, + QQmlBinding::expressionChanged +}; + +QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContext *ctxt) +: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), m_lineNumber(-1), m_columnNumber(-1) { - setParent(parent); setNotifyOnValueChanged(true); + QQmlAbstractExpression::setContext(QQmlContextData::get(ctxt)); + setScopeObject(obj); + + QQmlRewrite::RewriteBinding rewriteBinding; + QString code = rewriteBinding(str); + + m_expression = str.toUtf8(); + v8function = evalFunction(context(), obj, code, QString(), 0); } -QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt, - QObject *parent) -: QQmlExpression(ctxt, obj, str, *new QQmlBindingPrivate) +QQmlBinding::QQmlBinding(const QString &str, QObject *obj, QQmlContextData *ctxt) +: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), m_lineNumber(-1), m_columnNumber(-1) { - setParent(parent); setNotifyOnValueChanged(true); + QQmlAbstractExpression::setContext(ctxt); + setScopeObject(obj); + + QQmlRewrite::RewriteBinding rewriteBinding; + QString code = rewriteBinding(str); + + m_expression = str.toUtf8(); + v8function = evalFunction(ctxt, obj, code, QString(), 0); } QQmlBinding::QQmlBinding(const QString &str, bool isRewritten, QObject *obj, - QQmlContextData *ctxt, - const QString &url, int lineNumber, int columnNumber, - QObject *parent) -: QQmlExpression(ctxt, obj, str, isRewritten, url, lineNumber, columnNumber, *new QQmlBindingPrivate) + QQmlContextData *ctxt, + const QString &url, int lineNumber, int columnNumber) +: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), m_lineNumber(-1), m_columnNumber(-1) { - setParent(parent); setNotifyOnValueChanged(true); + QQmlAbstractExpression::setContext(ctxt); + setScopeObject(obj); + + QString code; + if (isRewritten) { + code = str; + } else { + QQmlRewrite::RewriteBinding rewriteBinding; + code = rewriteBinding(str); + } + + m_url = url; + m_lineNumber = lineNumber; + m_columnNumber = columnNumber; + m_expression = str.toUtf8(); + + v8function = evalFunction(ctxt, obj, code, url, lineNumber); } /*! @@ -273,124 +149,100 @@ QQmlBinding::QQmlBinding(const QString &str, bool isRewritten, QObject *obj, v8::Handle<v8::Function> function; new QQmlBinding(&function, scope, ctxt); */ -QQmlBinding::QQmlBinding(void *functionPtr, QObject *obj, QQmlContextData *ctxt, - QObject *parent) -: QQmlExpression(ctxt, obj, functionPtr, *new QQmlBindingPrivate) +QQmlBinding::QQmlBinding(void *functionPtr, QObject *obj, QQmlContextData *ctxt, + const QString &url, int lineNumber, int columnNumber) +: QQmlJavaScriptExpression(&QQmlBinding_jsvtable), + m_url(url), m_lineNumber(lineNumber), m_columnNumber(columnNumber) { - setParent(parent); setNotifyOnValueChanged(true); -} - -QQmlBinding::~QQmlBinding() -{ -} - -void QQmlBinding::setTarget(const QQmlProperty &prop) -{ - Q_D(QQmlBinding); - d->property = prop; - d->target = d->property.object(); - d->targetProperty = QQmlPropertyPrivate::get(d->property)->core.encodedIndex(); + QQmlAbstractExpression::setContext(ctxt); + setScopeObject(obj); - update(); + v8function = qPersistentNew<v8::Function>(*(v8::Handle<v8::Function> *)functionPtr); } -void QQmlBinding::setTarget(QObject *object, - const QQmlPropertyData &core, - QQmlContextData *ctxt) +QQmlBinding::~QQmlBinding() { - Q_D(QQmlBinding); - d->property = QQmlPropertyPrivate::restore(object, core, ctxt); - d->target = d->property.object(); - d->targetProperty = QQmlPropertyPrivate::get(d->property)->core.encodedIndex(); - - update(); + qPersistentDispose(v8function); } -QQmlProperty QQmlBinding::property() const +void QQmlBinding::setEvaluateFlags(EvaluateFlags flags) { - Q_D(const QQmlBinding); - return d->property; + setRequiresThisObject(flags & RequiresThisObject); } -void QQmlBinding::setEvaluateFlags(EvaluateFlags flags) +QQmlBinding::EvaluateFlags QQmlBinding::evaluateFlags() const { - Q_D(QQmlBinding); - d->setRequiresThisObject(flags & RequiresThisObject); + return requiresThisObject()?RequiresThisObject:None; } -QQmlBinding::EvaluateFlags QQmlBinding::evaluateFlags() const +void QQmlBinding::setNotifyOnValueChanged(bool v) { - Q_D(const QQmlBinding); - return d->requiresThisObject()?RequiresThisObject:None; + QQmlJavaScriptExpression::setNotifyOnValueChanged(v); } void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) { - Q_D(QQmlBinding); - - if (!d->enabled || !d->context() || !d->context()->isValid()) + if (!enabledFlag() || !context() || !context()->isValid()) return; QQmlTrace trace("General Binding Update"); - trace.addDetail("URL", d->url); - trace.addDetail("Line", d->line); - trace.addDetail("Column", d->columnNumber); + trace.addDetail("URL", m_url); + trace.addDetail("Line", m_lineNumber); + trace.addDetail("Column", m_columnNumber); - if (!d->updating) { - QQmlBindingProfiler prof(d->url, d->line, d->column); + if (!updatingFlag()) { + QQmlBindingProfiler prof(m_url, m_lineNumber, m_columnNumber); if (prof.enabled) prof.addDetail(expression()); - d->updating = true; + setUpdatingFlag(true); - QQmlAbstractExpression::DeleteWatcher watcher(d); + QQmlAbstractExpression::DeleteWatcher watcher(this); - if (d->property.propertyType() == qMetaTypeId<QQmlBinding *>()) { + if (m_core.propType == qMetaTypeId<QQmlBinding *>()) { - int idx = d->property.index(); + int idx = m_core.coreIndex; Q_ASSERT(idx != -1); QQmlBinding *t = this; int status = -1; void *a[] = { &t, 0, &status, &flags }; - QMetaObject::metacall(d->property.object(), - QMetaObject::WriteProperty, - idx, a); + QMetaObject::metacall(*m_coreObject, QMetaObject::WriteProperty, idx, a); } else { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(d->context()->engine); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); ep->referenceScarceResources(); bool isUndefined = false; v8::HandleScope handle_scope; v8::Context::Scope scope(ep->v8engine()->context()); - v8::Local<v8::Value> result = d->v8value(0, &isUndefined); + v8::Local<v8::Value> result = + QQmlJavaScriptExpression::evaluate(context(), v8function, &isUndefined); trace.event("writing binding result"); bool needsErrorData = false; - if (!watcher.wasDeleted() && !d->hasError()) - needsErrorData = !QQmlPropertyPrivate::writeBinding(d->property, d->context(), - d, result, - isUndefined, flags); + if (!watcher.wasDeleted() && !hasError()) + needsErrorData = !QQmlPropertyPrivate::writeBinding(*m_coreObject, m_core, context(), + this, result, isUndefined, flags); if (!watcher.wasDeleted()) { if (needsErrorData) { - QUrl url = QUrl(d->url); - int line = d->line; + QUrl url = QUrl(m_url); + if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>")); - d->delayedError()->error.setUrl(url); - d->delayedError()->error.setLine(line); - d->delayedError()->error.setColumn(-1); + delayedError()->error.setUrl(url); + delayedError()->error.setLine(m_lineNumber); + delayedError()->error.setColumn(m_columnNumber); } - if (d->hasError()) { - if (!d->delayedError()->addError(ep)) ep->warning(this->error()); + if (hasError()) { + if (!delayedError()->addError(ep)) ep->warning(this->error()); } else { - d->clearError(); + clearError(); } } @@ -399,154 +251,96 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags) } if (!watcher.wasDeleted()) - d->updating = false; + setUpdatingFlag(false); } else { - QQmlBindingPrivate::printBindingLoopError(d->property); + QQmlProperty p = property(); + QQmlAbstractBinding::printBindingLoopError(p); } } -void QQmlBindingPrivate::printBindingLoopError(QQmlProperty &prop) +QVariant QQmlBinding::evaluate() { - qmlInfo(prop.object()) << QQmlBinding::tr("Binding loop detected for property \"%1\"").arg(prop.name()); -} + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); + ep->referenceScarceResources(); -void QQmlBindingPrivate::expressionChanged() -{ - Q_Q(QQmlBinding); - q->update(); -} + bool isUndefined = false; -void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) -{ - Q_D(QQmlBinding); - d->enabled = e; - setNotifyOnValueChanged(e); - - if (e) - update(flags); -} + v8::HandleScope handle_scope; + v8::Context::Scope scope(ep->v8engine()->context()); + v8::Local<v8::Value> result = + QQmlJavaScriptExpression::evaluate(context(), v8function, &isUndefined); -bool QQmlBinding::enabled() const -{ - Q_D(const QQmlBinding); + ep->dereferenceScarceResources(); - return d->enabled; + return ep->v8engine()->toVariant(result, qMetaTypeId<QList<QObject*> >()); } -QString QQmlBinding::expression() const +QString QQmlBinding::expressionIdentifier(QQmlJavaScriptExpression *e) { - return QQmlExpression::expression(); -} + QQmlBinding *This = static_cast<QQmlBinding *>(e); -int QQmlBinding::propertyIndex() const -{ - Q_D(const QQmlBinding); - return d->targetProperty; + return QLatin1String("\"") + QString::fromUtf8(This->m_expression) + QLatin1String("\""); } -QObject *QQmlBinding::object() const -{ - Q_D(const QQmlBinding); - return d->target; -} - -void QQmlBinding::retargetBinding(QObject *t, int i) +void QQmlBinding::expressionChanged(QQmlJavaScriptExpression *e) { - Q_D(QQmlBinding); - d->target = t; - d->targetProperty = i; + QQmlBinding *This = static_cast<QQmlBinding *>(e); + This->update(); } -QQmlValueTypeProxyBinding::QQmlValueTypeProxyBinding(QObject *o, int index) -: m_object(o), m_index(index), m_bindings(0) +void QQmlBinding::refresh() { + update(); } -QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding() +void QQmlBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) { - while (m_bindings) { - QQmlAbstractBinding *binding = m_bindings; - binding->setEnabled(false, 0); - binding->destroy(); - } -} + setEnabledFlag(e); + setNotifyOnValueChanged(e); -void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) -{ - if (e) { - QQmlAbstractBinding *bindings = m_bindings; - recursiveEnable(bindings, flags); - } else { - QQmlAbstractBinding *bindings = m_bindings; - recursiveDisable(bindings); - } + if (e) + update(flags); } -void QQmlValueTypeProxyBinding::recursiveEnable(QQmlAbstractBinding *b, QQmlPropertyPrivate::WriteFlags flags) +QString QQmlBinding::expression() const { - if (!b) - return; - - recursiveEnable(b->m_nextBinding, flags); - - if (b) - b->setEnabled(true, flags); + return QString::fromUtf8(m_expression); } -void QQmlValueTypeProxyBinding::recursiveDisable(QQmlAbstractBinding *b) +QObject *QQmlBinding::object() const { - if (!b) - return; - - recursiveDisable(b->m_nextBinding); - - if (b) - b->setEnabled(false, 0); + if (m_coreObject.hasValue()) return m_coreObject.constValue()->target; + else return *m_coreObject; } -void QQmlValueTypeProxyBinding::update(QQmlPropertyPrivate::WriteFlags) +int QQmlBinding::propertyIndex() const { + if (m_coreObject.hasValue()) return m_coreObject.constValue()->targetProperty; + else return m_core.encodedIndex(); } -QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex) +void QQmlBinding::retargetBinding(QObject *t, int i) { - QQmlAbstractBinding *binding = m_bindings; - - while (binding && binding->propertyIndex() != propertyIndex) - binding = binding->m_nextBinding; - - return binding; + m_coreObject.value().target = t; + m_coreObject.value().targetProperty = i; } -/*! -Removes a collection of bindings, corresponding to the set bits in \a mask. -*/ -void QQmlValueTypeProxyBinding::removeBindings(quint32 mask) +void QQmlBinding::setTarget(const QQmlProperty &prop) { - 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; - } - } + setTarget(prop.object(), QQmlPropertyPrivate::get(prop)->core, + QQmlPropertyPrivate::get(prop)->context); } -int QQmlValueTypeProxyBinding::propertyIndex() const +void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, QQmlContextData *ctxt) { - return m_index; + m_coreObject = object; + m_core = core; + m_ctxt = ctxt; } -QObject *QQmlValueTypeProxyBinding::object() const +QQmlProperty QQmlBinding::property() const { - return m_object; + return QQmlPropertyPrivate::restore(object(), m_core, *m_ctxt); } QT_END_NAMESPACE |