summaryrefslogtreecommitdiffstats
path: root/src/declarative/qml/qmlexpression.cpp
diff options
context:
space:
mode:
authorAaron Kennedy <aaron.kennedy@nokia.com>2009-07-20 06:06:19 (GMT)
committerAaron Kennedy <aaron.kennedy@nokia.com>2009-07-20 08:33:23 (GMT)
commit86529642cfdc5cc5a94b735042d0807e3b57e9e1 (patch)
tree3df356de955a131abfed2c84db4f91abf639a583 /src/declarative/qml/qmlexpression.cpp
parent275d2a37c0c7179e7f34cc467192a8838705d7c2 (diff)
downloadQt-86529642cfdc5cc5a94b735042d0807e3b57e9e1.zip
Qt-86529642cfdc5cc5a94b735042d0807e3b57e9e1.tar.gz
Qt-86529642cfdc5cc5a94b735042d0807e3b57e9e1.tar.bz2
Rework expression stuff to use a more efficient notify handler
Diffstat (limited to 'src/declarative/qml/qmlexpression.cpp')
-rw-r--r--src/declarative/qml/qmlexpression.cpp412
1 files changed, 176 insertions, 236 deletions
diff --git a/src/declarative/qml/qmlexpression.cpp b/src/declarative/qml/qmlexpression.cpp
index 2aa1a8a..b4f57eb 100644
--- a/src/declarative/qml/qmlexpression.cpp
+++ b/src/declarative/qml/qmlexpression.cpp
@@ -50,53 +50,72 @@ Q_DECLARE_METATYPE(QList<QObject *>);
QT_BEGIN_NAMESPACE
-DEFINE_BOOL_CONFIG_OPTION(qmlDebugger, QML_DEBUGGER)
-
-QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b)
-: q(b), ctxt(0), expressionFunctionValid(false), sseData(0), proxy(0), me(0), trackChange(false), line(-1), id(0), log(0)
+QmlExpressionPrivate::QmlExpressionPrivate()
+: ctxt(0), expressionFunctionValid(false), sseData(0), me(0), trackChange(true), line(-1), id(0), guardList(0), guardListLength(0)
{
}
-QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b, void *expr, QmlRefCount *rc)
-: q(b), ctxt(0), expressionFunctionValid(false), sse((const char *)expr, rc), sseData(0), proxy(0), me(0), trackChange(true), line(-1), id(0), log(0)
+void QmlExpressionPrivate::init(QmlContext *ctxt, const QString &expr,
+ QObject *me)
{
+ Q_Q(QmlExpression);
+
+ expression = expr;
+
+ this->ctxt = ctxt;
+ if (ctxt && ctxt->engine())
+ id = ctxt->engine()->d_func()->getUniqueId();
+ if (ctxt)
+ ctxt->d_func()->childExpressions.insert(q);
+ this->me = me;
}
-QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b, const QString &expr)
-: q(b), ctxt(0), expression(expr), expressionFunctionValid(false), sseData(0), proxy(0), me(0), trackChange(true), line(-1), id(0), log(0)
+void QmlExpressionPrivate::init(QmlContext *ctxt, void *expr, QmlRefCount *rc,
+ QObject *me)
{
+ Q_Q(QmlExpression);
+
+ sse.load((const char *)expr, rc);
+
+ this->ctxt = ctxt;
+ if (ctxt && ctxt->engine())
+ id = ctxt->engine()->d_func()->getUniqueId();
+ if (ctxt)
+ ctxt->d_func()->childExpressions.insert(q);
+ this->me = me;
}
QmlExpressionPrivate::~QmlExpressionPrivate()
{
sse.deleteScriptState(sseData);
sseData = 0;
- delete proxy;
- delete log;
+ if (guardList) { delete [] guardList; guardList = 0; }
}
/*!
+ \class QmlExpression
+ \brief The QmlExpression class evaluates ECMAScript in a QML context.
+*/
+
+/*!
Create an invalid QmlExpression.
As the expression will not have an associated QmlContext, this will be a
null expression object and its value will always be an invalid QVariant.
*/
QmlExpression::QmlExpression()
-: d(new QmlExpressionPrivate(this))
+: QObject(*new QmlExpressionPrivate, 0)
{
}
-/*! \internal */
+/*! \internal */
QmlExpression::QmlExpression(QmlContext *ctxt, void *expr,
- QmlRefCount *rc, QObject *me)
-: d(new QmlExpressionPrivate(this, expr, rc))
+ QmlRefCount *rc, QObject *me,
+ QmlExpressionPrivate &dd)
+: QObject(dd, 0)
{
- d->ctxt = ctxt;
- if(ctxt && ctxt->engine())
- d->id = ctxt->engine()->d_func()->getUniqueId();
- if(ctxt)
- ctxt->d_func()->childExpressions.insert(this);
- d->me = me;
+ Q_D(QmlExpression);
+ d->init(ctxt, expr, rc, me);
}
/*!
@@ -108,14 +127,19 @@ QmlExpression::QmlExpression(QmlContext *ctxt, void *expr,
*/
QmlExpression::QmlExpression(QmlContext *ctxt, const QString &expression,
QObject *scope)
-: d(new QmlExpressionPrivate(this, expression))
+: QObject(*new QmlExpressionPrivate, 0)
+{
+ Q_D(QmlExpression);
+ d->init(ctxt, expression, scope);
+}
+
+/*! \internal */
+QmlExpression::QmlExpression(QmlContext *ctxt, const QString &expression,
+ QObject *scope, QmlExpressionPrivate &dd)
+: QObject(dd, 0)
{
- d->ctxt = ctxt;
- if(ctxt && ctxt->engine())
- d->id = ctxt->engine()->d_func()->getUniqueId();
- if(ctxt)
- ctxt->d_func()->childExpressions.insert(this);
- d->me = scope;
+ Q_D(QmlExpression);
+ d->init(ctxt, expression, scope);
}
/*!
@@ -123,9 +147,9 @@ QmlExpression::QmlExpression(QmlContext *ctxt, const QString &expression,
*/
QmlExpression::~QmlExpression()
{
+ Q_D(QmlExpression);
if (d->ctxt)
d->ctxt->d_func()->childExpressions.remove(this);
- delete d; d = 0;
}
/*!
@@ -134,6 +158,7 @@ QmlExpression::~QmlExpression()
*/
QmlEngine *QmlExpression::engine() const
{
+ Q_D(const QmlExpression);
return d->ctxt?d->ctxt->engine():0;
}
@@ -143,6 +168,7 @@ QmlEngine *QmlExpression::engine() const
*/
QmlContext *QmlExpression::context() const
{
+ Q_D(const QmlExpression);
return d->ctxt;
}
@@ -151,6 +177,7 @@ QmlContext *QmlExpression::context() const
*/
QString QmlExpression::expression() const
{
+ Q_D(const QmlExpression);
if (d->sse.isValid())
return QLatin1String(d->sse.expression());
else
@@ -170,12 +197,13 @@ void QmlExpression::clearExpression()
*/
void QmlExpression::setExpression(const QString &expression)
{
+ Q_D(QmlExpression);
if (d->sseData) {
d->sse.deleteScriptState(d->sseData);
d->sseData = 0;
}
- delete d->proxy; d->proxy = 0;
+ d->clearGuards();
d->expression = expression;
d->expressionFunctionValid = false;
@@ -184,18 +212,6 @@ void QmlExpression::setExpression(const QString &expression)
d->sse.clear();
}
-/*!
- Called by QmlExpression each time the expression value changes from the
- last time it was evaluated. The expression must have been evaluated at
- least once (by calling QmlExpression::value()) before this callback will
- be made.
-
- The default implementation does nothing.
-*/
-void QmlExpression::valueChanged()
-{
-}
-
QVariant QmlExpressionPrivate::evalSSE(QmlBasicScript::CacheState &cacheState)
{
#ifdef Q_ENABLE_PERFORMANCE_LOG
@@ -318,6 +334,8 @@ QVariant QmlExpressionPrivate::evalQtScript()
*/
QVariant QmlExpression::value()
{
+ Q_D(QmlExpression);
+
QVariant rv;
if (!d->ctxt || !engine() || (!d->sse.isValid() && d->expression.isEmpty()))
return rv;
@@ -340,87 +358,10 @@ QVariant QmlExpression::value()
ep->currentExpression = lastCurrentExpression;
- if (cacheState != QmlBasicScript::NoChange) {
- if (cacheState != QmlBasicScript::Incremental && d->proxy) {
- delete d->proxy;
- d->proxy = 0;
- }
-
- if (trackChange() && ep->capturedProperties.count()) {
- if (!d->proxy)
- d->proxy = new QmlExpressionBindProxy(this);
-
- static int changedIndex = -1;
- if (changedIndex == -1)
- changedIndex = QmlExpressionBindProxy::staticMetaObject.indexOfSlot("changed()");
-
- if(qmlDebugger()) {
- QmlExpressionLog log;
- log.setTime(engine()->d_func()->getUniqueId());
- log.setExpression(expression());
- log.setResult(rv);
-
- for (int ii = 0; ii < ep->capturedProperties.count(); ++ii) {
- const QmlEnginePrivate::CapturedProperty &prop =
- ep->capturedProperties.at(ii);
-
- if (prop.notifyIndex != -1) {
- QMetaObject::connect(prop.object, prop.notifyIndex,
- d->proxy, changedIndex);
- } else {
- const QMetaObject *metaObj = prop.object->metaObject();
- QMetaProperty metaProp =
- metaObj->property(prop.coreIndex);
-
- QString warn = QLatin1String("Expression depends on non-NOTIFYable property: ") +
- QLatin1String(metaObj->className()) +
- QLatin1String("::") +
- QLatin1String(metaProp.name());
- log.addWarning(warn);
- }
- }
- d->addLog(log);
-
- } else {
- bool outputWarningHeader = false;
- for (int ii = 0; ii < ep->capturedProperties.count(); ++ii) {
- const QmlEnginePrivate::CapturedProperty &prop =
- ep->capturedProperties.at(ii);
-
- if (prop.notifyIndex != -1) {
- QMetaObject::connect(prop.object, prop.notifyIndex,
- d->proxy, changedIndex);
- } else {
- if (!outputWarningHeader) {
- outputWarningHeader = true;
- qWarning() << "QmlExpression: Expression" << expression() << "depends on non-NOTIFYable properties:";
- }
-
- const QMetaObject *metaObj = prop.object->metaObject();
- QMetaProperty metaProp =
- metaObj->property(prop.coreIndex);
-
- qWarning().nospace() << " " << metaObj->className()
- << "::" << metaProp.name();
- }
- }
- }
- } else {
- QmlExpressionLog log;
- log.setTime(engine()->d_func()->getUniqueId());
- log.setExpression(expression());
- log.setResult(rv);
- d->addLog(log);
- }
-
- } else {
- if(qmlDebugger()) {
- QmlExpressionLog log;
- log.setTime(engine()->d_func()->getUniqueId());
- log.setExpression(expression());
- log.setResult(rv);
- d->addLog(log);
- }
+ if ((!trackChange() || !ep->capturedProperties.count()) && d->guardList) {
+ d->clearGuards();
+ } else if(trackChange()) {
+ d->updateGuards(ep->capturedProperties);
}
ep->capturedProperties.clear();
@@ -435,7 +376,8 @@ QVariant QmlExpression::value()
*/
bool QmlExpression::isConstant() const
{
- return d->proxy == 0;
+ Q_D(const QmlExpression);
+ return !d->guardList;
}
/*!
@@ -443,6 +385,7 @@ bool QmlExpression::isConstant() const
*/
bool QmlExpression::trackChange() const
{
+ Q_D(const QmlExpression);
return d->trackChange;
}
@@ -463,6 +406,7 @@ bool QmlExpression::trackChange() const
*/
void QmlExpression::setTrackChange(bool trackChange)
{
+ Q_D(QmlExpression);
d->trackChange = trackChange;
}
@@ -472,6 +416,7 @@ void QmlExpression::setTrackChange(bool trackChange)
*/
void QmlExpression::setSourceLocation(const QUrl &fileName, int line)
{
+ Q_D(QmlExpression);
d->fileName = fileName;
d->line = line;
}
@@ -484,6 +429,7 @@ void QmlExpression::setSourceLocation(const QUrl &fileName, int line)
*/
QObject *QmlExpression::scopeObject() const
{
+ Q_D(const QmlExpression);
return d->me;
}
@@ -492,144 +438,138 @@ QObject *QmlExpression::scopeObject() const
*/
quint32 QmlExpression::id() const
{
+ Q_D(const QmlExpression);
return d->id;
}
-/*!
- \class QmlExpression
- \brief The QmlExpression class evaluates ECMAScript in a QML context.
-*/
-
-/*!
- \class QmlExpressionObject
- \brief The QmlExpressionObject class extends QmlExpression with signals and slots.
-
- To remain as lightweight as possible, QmlExpression does not inherit QObject
- and consequently cannot use signals or slots. For the cases where this is
- more convenient in an application, QmlExpressionObject can be used instead.
-
- QmlExpressionObject behaves identically to QmlExpression, except that the
- QmlExpressionObject::value() method is a slot, and the
- QmlExpressionObject::valueChanged() callback is a signal.
-*/
-/*!
- Create a QmlExpression with the specified \a parent.
-
- As the expression will not have an associated QmlContext, this will be a
- null expression object and its value will always be an invalid QVariant.
-*/
-QmlExpressionObject::QmlExpressionObject(QObject *parent)
-: QObject(parent)
-{
-}
-
-/*!
- Create a QmlExpressionObject with the specified \a parent.
-
- The \a expression ECMAScript will be executed in the \a ctxt QmlContext.
- If specified, the \a scope object's properties will also be in scope during
- the expression's execution.
-*/
-QmlExpressionObject::QmlExpressionObject(QmlContext *ctxt, const QString &expression, QObject *scope, QObject *parent)
-: QObject(parent), QmlExpression(ctxt, expression, scope)
-{
-}
-
-/*! \internal */
-QmlExpressionObject::QmlExpressionObject(QmlContext *ctxt, void *d, QmlRefCount *rc, QObject *me)
-: QmlExpression(ctxt, d, rc, me)
+/*! \internal */
+void QmlExpression::__q_notify()
{
+ valueChanged();
}
-/*!
- Returns the value of the expression, or an invalid QVariant if the
- expression is invalid or has an error.
-*/
-QVariant QmlExpressionObject::value()
+void QmlExpressionPrivate::clearGuards()
{
- return QmlExpression::value();
-}
-
-/*!
- \fn void QmlExpressionObject::valueChanged()
+ Q_Q(QmlExpression);
- Emitted each time the expression value changes from the last time it was
- evaluated. The expression must have been evaluated at least once (by
- calling QmlExpressionObject::value()) before this signal will be emitted.
-*/
+ static int notifyIdx = -1;
+ if (notifyIdx == -1)
+ notifyIdx =
+ QmlExpression::staticMetaObject.indexOfMethod("__q_notify()");
-void QmlExpressionPrivate::addLog(const QmlExpressionLog &l)
-{
- if (!log)
- log = new QList<QmlExpressionLog>();
- log->append(l);
-}
+ for (int ii = 0; ii < guardListLength; ++ii) {
+ if (guardList[ii].data()) {
+ QMetaObject::disconnect(guardList[ii].data(),
+ guardList[ii].notifyIndex,
+ q, notifyIdx);
+ }
+ }
-QmlExpressionLog::QmlExpressionLog()
-{
+ delete [] guardList; guardList = 0;
+ guardListLength = 0;
}
-QmlExpressionLog::QmlExpressionLog(const QmlExpressionLog &o)
-: m_time(o.m_time),
- m_expression(o.m_expression),
- m_result(o.m_result),
- m_warnings(o.m_warnings)
+void QmlExpressionPrivate::updateGuards(const QPODVector<QmlEnginePrivate::CapturedProperty> &properties)
{
-}
+ Q_Q(QmlExpression);
-QmlExpressionLog::~QmlExpressionLog()
-{
-}
+ static int notifyIdx = -1;
+ if (notifyIdx == -1)
+ notifyIdx =
+ QmlExpression::staticMetaObject.indexOfMethod("__q_notify()");
-QmlExpressionLog &QmlExpressionLog::operator=(const QmlExpressionLog &o)
-{
- m_time = o.m_time;
- m_expression = o.m_expression;
- m_result = o.m_result;
- m_warnings = o.m_warnings;
- return *this;
-}
+ SignalGuard *newGuardList = 0;
+
+ if (properties.count() != guardListLength)
+ newGuardList = new SignalGuard[properties.count()];
-void QmlExpressionLog::setTime(quint32 time)
-{
- m_time = time;
-}
+ bool outputWarningHeader = false;
+ int hit = 0;
+ for (int ii = 0; ii < properties.count(); ++ii) {
+ const QmlEnginePrivate::CapturedProperty &property = properties.at(ii);
-quint32 QmlExpressionLog::time() const
-{
- return m_time;
-}
+ bool needGuard = true;
+ if (ii >= guardListLength) {
+ // New guard
+ } else if(guardList[ii].data() == property.object &&
+ guardList[ii].notifyIndex == property.notifyIndex) {
+ // Cache hit
+ if (!guardList[ii].isDuplicate ||
+ (guardList[ii].isDuplicate && hit == ii)) {
+ needGuard = false;
+ ++hit;
+ }
+ } else if(guardList[ii].data()) {
+ // Cache miss
+ QMetaObject::disconnect(guardList[ii].data(),
+ guardList[ii].notifyIndex,
+ q, notifyIdx);
+ }
+ /* else {
+ // Cache miss, but nothing to do
+ } */
+
+ if (needGuard) {
+ if (!newGuardList) {
+ newGuardList = new SignalGuard[properties.count()];
+ for (int jj = 0; jj < ii; ++jj)
+ newGuardList[jj] = guardList[jj];
+ }
-QString QmlExpressionLog::expression() const
-{
- return m_expression;
-}
+ if (property.notifyIndex != -1) {
+ bool existing = false;
+ for (int jj = 0; !existing && jj < ii; ++jj)
+ existing = newGuardList[jj].data() == property.object &&
+ newGuardList[jj].notifyIndex == property.notifyIndex;
+
+ newGuardList[ii] = property.object;
+ newGuardList[ii].notifyIndex = property.notifyIndex;
+ if (existing)
+ newGuardList[ii].isDuplicate = true;
+ else
+ QMetaObject::connect(property.object, property.notifyIndex,
+ q, notifyIdx);
+ } else {
+ if (!outputWarningHeader) {
+ outputWarningHeader = true;
+ qWarning() << "QmlExpression: Expression" << q->expression()
+ << "depends on non-NOTIFYable properties:";
+ }
-void QmlExpressionLog::setExpression(const QString &e)
-{
- m_expression = e;
-}
+ const QMetaObject *metaObj = property.object->metaObject();
+ QMetaProperty metaProp = metaObj->property(property.coreIndex);
-QStringList QmlExpressionLog::warnings() const
-{
- return m_warnings;
-}
+ qWarning().nospace() << " " << metaObj->className()
+ << "::" << metaProp.name();
+ }
+ } else if (newGuardList) {
+ newGuardList[ii] = guardList[ii];
+ }
+ }
-void QmlExpressionLog::addWarning(const QString &w)
-{
- m_warnings << w;
-}
+ for (int ii = properties.count(); ii < guardListLength; ++ii) {
+ if (guardList[ii].data()) {
+ QMetaObject::disconnect(guardList[ii].data(),
+ guardList[ii].notifyIndex,
+ q, notifyIdx);
+ }
+ }
-QVariant QmlExpressionLog::result() const
-{
- return m_result;
+ if (newGuardList) {
+ if (guardList) delete [] guardList;
+ guardList = newGuardList;
+ guardListLength = properties.count();
+ }
+ //qWarning() << hit << properties.count() << q->expression();
}
-void QmlExpressionLog::setResult(const QVariant &r)
-{
- m_result = r;
-}
+/*!
+ \fn void QmlExpression::valueChanged()
+ Emitted each time the expression value changes from the last time it was
+ evaluated. The expression must have been evaluated at least once (by
+ calling QmlExpression::value()) before this signal will be emitted.
+*/
QT_END_NAMESPACE