From 9ab8b9e4bdf313bb4304be206dd479d4d0848211 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 5 Apr 2016 13:43:03 +0200 Subject: QML: Do not register static QML dependencies on every call. QML objects can be re-parented on the fly, resulting in different dependencies for expressions like 'parent.width'. So, because of this, dependencies are cleared and re-calculated after every binding evaluation. However, dependencies on properties of the scope and context objects cannot change, because these objects do not get changed for the life-time of a binding. So we can permanently register them. This is only done for bindings, not for functions, because those might be conditionally executed. According to valgrind, this is a reduction of ~186 instructions on x86 for every evaluation of: Item { height: width } Change-Id: Ib095497323d4f08caf712d480007e2627a176369 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 4 ++- src/qml/compiler/qv4codegen.cpp | 1 + src/qml/compiler/qv4jsir.cpp | 1 + src/qml/compiler/qv4jsir_p.h | 3 ++- src/qml/jsruntime/qv4functionobject.cpp | 2 +- src/qml/qml/qqmljavascriptexpression.cpp | 43 ++++++++++++++++++++++++-------- src/qml/qml/qqmljavascriptexpression_p.h | 13 +++++++--- 7 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 4fa63e3c84..c31a7bce4f 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1890,7 +1890,9 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int // Look for IDs first. foreach (const IdMapping &mapping, _idObjects) if (name == mapping.name) { - _function->idObjectDependencies.insert(mapping.idIndex); + if (_function->isQmlBinding) + _function->idObjectDependencies.insert(mapping.idIndex); + QV4::IR::Expr *s = _block->MEMBER(_block->TEMP(_qmlContextTemp), _function->newString(name), 0, QV4::IR::Member::MemberOfIdObjectsArray, mapping.idIndex); QV4::IR::Temp *result = _block->TEMP(_block->newTemp()); _block->MOVE(result, s); diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 8711bc049a..7a444e8c57 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1961,6 +1961,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, function->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); function->isStrict = _env->isStrict; function->isNamedExpression = _env->isNamedFunctionExpression; + function->isQmlBinding = _env->compilationMode == QmlBinding; AST::SourceLocation loc = ast->firstSourceLocation(); function->line = loc.startLine; diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index b994d2f707..74c4f91f2b 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -368,6 +368,7 @@ Function::Function(Module *module, Function *outer, const QString &name) , isNamedExpression(false) , hasTry(false) , hasWith(false) + , isQmlBinding(false) , unused(0) , line(-1) , column(-1) diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 0254ae224d..c196e8127b 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -1258,7 +1258,8 @@ struct Function { uint isNamedExpression : 1; uint hasTry: 1; uint hasWith: 1; - uint unused : 25; + uint isQmlBinding: 1; + uint unused : 24; // Location of declaration in source code (-1 if not specified) int line; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 041044930b..fce80c46eb 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -435,7 +435,7 @@ void ScriptFunction::construct(const Managed *that, Scope &scope, CallData *call Scoped f(scope, static_cast(that)); - InternalClass *ic = scope.engine->emptyClass; + InternalClass *ic = v4->emptyClass; ScopedObject proto(scope, f->protoForConstructor()); ScopedObject obj(scope, v4->newObject(ic, proto)); diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index eff1a6e039..7fb66a49bf 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -106,7 +106,8 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() m_nextExpression->m_prevExpression = m_prevExpression; } - clearGuards(); + clearActiveGuards(); + clearPermanentGuards(); if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion. m_scopeObject.asT2()->_s = 0; } @@ -114,12 +115,12 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v) { activeGuards.setFlagValue(v); - if (!v) clearGuards(); + if (!v) clearActiveGuards(); } void QQmlJavaScriptExpression::resetNotifyOnValueChanged() { - clearGuards(); + clearActiveGuards(); } void QQmlJavaScriptExpression::setContext(QQmlContextData *context) @@ -214,7 +215,7 @@ void QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, bool *isUndefin ep->propertyCapture = lastPropertyCapture; } -void QQmlPropertyCapture::captureProperty(QQmlNotifier *n) +void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration) { if (watcher->wasDeleted()) return; @@ -234,14 +235,17 @@ void QQmlPropertyCapture::captureProperty(QQmlNotifier *n) g->connect(n); } - expression->activeGuards.prepend(g); + if (duration == OnlyOnce) + expression->permanentGuards.prepend(g); + else + expression->activeGuards.prepend(g); } /*! \internal \a n is in the signal index range (see QObjectPrivate::signalIndex()). */ -void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n) +void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration duration) { if (watcher->wasDeleted()) return; @@ -280,7 +284,10 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n) g->connect(o, n, engine); } - expression->activeGuards.prepend(g); + if (duration == Permanently) + expression->permanentGuards.prepend(g); + else + expression->activeGuards.prepend(g); } } @@ -297,6 +304,11 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct if (!capture) return; + if (capture->expression->m_permanentDependenciesRegistered) + return; + + capture->expression->m_permanentDependenciesRegistered = true; + QV4::Scoped context(scope, engine->qmlContext()); QQmlContextData *qmlContext = context->qmlContext(); @@ -304,7 +316,8 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct const int idObjectDependencyCount = compiledFunction->nDependingIdObjects; for (int i = 0; i < idObjectDependencyCount; ++i, ++idObjectDependency) { Q_ASSERT(int(*idObjectDependency) < qmlContext->idValueCount); - capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings); + capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings, + QQmlPropertyCapture::Permanently); } Q_ASSERT(qmlContext->contextObject); @@ -313,7 +326,8 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct for (int i = 0; i < contextPropertyDependencyCount; ++i) { const int propertyIndex = *contextPropertyDependency++; const int notifyIndex = *contextPropertyDependency++; - capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex); + capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex, + QQmlPropertyCapture::Permanently); } QObject *scopeObject = context->qmlScope(); @@ -322,7 +336,8 @@ void QQmlPropertyCapture::registerQmlDependencies(const QV4::CompiledData::Funct for (int i = 0; i < scopePropertyDependencyCount; ++i) { const int propertyIndex = *scopePropertyDependency++; const int notifyIndex = *scopePropertyDependency++; - capture->captureProperty(scopeObject, propertyIndex, notifyIndex); + capture->captureProperty(scopeObject, propertyIndex, notifyIndex, + QQmlPropertyCapture::Permanently); } } @@ -406,12 +421,18 @@ void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject * } -void QQmlJavaScriptExpression::clearGuards() +void QQmlJavaScriptExpression::clearActiveGuards() { while (QQmlJavaScriptExpressionGuard *g = activeGuards.takeFirst()) g->Delete(); } +void QQmlJavaScriptExpression::clearPermanentGuards() +{ + while (QQmlJavaScriptExpressionGuard *g = permanentGuards.takeFirst()) + g->Delete(); +} + void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **) { QQmlJavaScriptExpression *expression = diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 4472a643ff..a5c7a80153 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -136,7 +136,8 @@ public: inline bool hasDelayedError() const; QQmlError error(QQmlEngine *) const; void clearError(); - void clearGuards(); + void clearActiveGuards(); + void clearPermanentGuards(); QQmlDelayedError *delayedError(); static QV4::ReturnedValue evalFunction(QQmlContextData *ctxt, QObject *scope, @@ -157,10 +158,12 @@ private: // activeGuards:flag2 - useSharedContext QBiPointer m_scopeObject; QForwardFieldList activeGuards; + QForwardFieldList permanentGuards; QQmlContextData *m_context; QQmlJavaScriptExpression **m_prevExpression; QQmlJavaScriptExpression *m_nextExpression; + bool m_permanentDependenciesRegistered = false; protected: QV4::PersistentValue m_function; @@ -177,10 +180,14 @@ public: Q_ASSERT(errorString == 0); } - void captureProperty(QQmlNotifier *); - void captureProperty(QObject *, int, int); + enum Duration { + OnlyOnce, + Permanently + }; static void registerQmlDependencies(const QV4::CompiledData::Function *compiledFunction, const QV4::Scope &scope); + void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce); + void captureProperty(QObject *, int, int, Duration duration = OnlyOnce); QQmlEngine *engine; QQmlJavaScriptExpression *expression; -- cgit v1.2.3