diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2018-05-14 15:21:10 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2018-05-16 14:57:59 +0000 |
commit | 7cb6dce1f3e140ea68d6b05281950f212fc99d38 (patch) | |
tree | bc31a800f853f54415823bd16c1f7fdb59f50e0c /src/qml | |
parent | c248a32fe69dfe1c685105d0c6aeaeb15d7ba29f (diff) |
Remove bindings that have no dependencies
After the initial enabling of a binding we can quickly determine if
there is a chance whether this binding will be re-evaluated again in the
future as a consequence of dependency changes (properties or
translations). If that is not the case, then we can save memory by
removing that binding again.
One implementation consequence of this change is that such constant
bindings used with the "when" property of states require a proper
reference count increase, which was previously implicit through the
binding association with the target object.
In tst_qqmlecmascript a test that verifies that we don't create run-time
bindings for assignments of literal "null" to QObject pointer properties
now also covers the more complex case where we don't know the property
at parse time. We still evaluate the binding once though and perform one
property assignment.
Similarly on the QtQuick Designer Support API test side a binding such
as
x: Math.max(0, 200)
will not create a persistent binding anymore and needs a tweak to
remain.
On a large scale application this optimization saved up to 5% of all
bindings on start-up (~9000 of ~180000). On Linux x86-64 one binding is
approximately 144 bytes, so the savings are in the range of ~1.2 MB of
heap, as well as reduced fragmentation.
Task-number: QTBUG-64541
Change-Id: Id3653008346fdf36611f5b4c4e82f5f78b5319aa
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/qml')
-rw-r--r-- | src/qml/qml/ftw/qflagpointer_p.h | 8 | ||||
-rw-r--r-- | src/qml/qml/qqmlbinding.cpp | 7 | ||||
-rw-r--r-- | src/qml/qml/qqmlbinding_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmljavascriptexpression.cpp | 8 | ||||
-rw-r--r-- | src/qml/qml/qqmljavascriptexpression_p.h | 15 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 5 | ||||
-rw-r--r-- | src/qml/qml/qqmlvaluetypeproxybinding_p.h | 2 | ||||
-rw-r--r-- | src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 12 |
8 files changed, 49 insertions, 9 deletions
diff --git a/src/qml/qml/ftw/qflagpointer_p.h b/src/qml/qml/ftw/qflagpointer_p.h index 91ce74bec9..71b41cd30b 100644 --- a/src/qml/qml/ftw/qflagpointer_p.h +++ b/src/qml/qml/ftw/qflagpointer_p.h @@ -82,6 +82,8 @@ public: inline T *data() const; + inline explicit operator bool() const; + private: quintptr ptr_value = 0; @@ -230,6 +232,12 @@ T *QFlagPointer<T>::data() const return (T *)(ptr_value & ~FlagsMask); } +template<typename T> +QFlagPointer<T>::operator bool() const +{ + return data() != nullptr; +} + template<typename T, typename T2> QBiPointer<T, T2>::QBiPointer() { diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 5fcd38aa54..a4b3f1f4e4 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -374,6 +374,8 @@ public: } } + bool hasDependencies() const override final { return true; } + private: const QV4::CompiledData::Binding *m_binding; }; @@ -663,6 +665,11 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const return dependencies; } +bool QQmlBinding::hasDependencies() const +{ + return !permanentGuards.isEmpty() || !activeGuards.isEmpty() || translationsCaptured(); +} + class QObjectPointerBinding: public QQmlNonbindingBinding { QQmlMetaObject targetMetaObject; diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 6cd64c7ca0..f192de4342 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -118,6 +118,7 @@ public: * Call this method from the UI thread. */ QVector<QQmlProperty> dependencies() const; + virtual bool hasDependencies() const; protected: virtual void doUpdate(const DeleteWatcher &watcher, diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index f0a5f18a15..9f2a96d5d9 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -93,8 +93,7 @@ void QQmlDelayedError::catchJavaScriptException(QV4::ExecutionEngine *engine) QQmlJavaScriptExpression::QQmlJavaScriptExpression() - : m_error(nullptr), - m_context(nullptr), + : m_context(nullptr), m_prevExpression(nullptr), m_nextExpression(nullptr), m_v4Function(nullptr) @@ -247,6 +246,9 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b while (QQmlJavaScriptExpressionGuard *g = capture.guards.takeFirst()) g->Delete(); + if (!watcher.wasDeleted()) + setTranslationsCaptured(capture.translationCaptured); + ep->propertyCapture = lastPropertyCapture; return result->asReturnedValue(); @@ -392,7 +394,7 @@ QQmlDelayedError *QQmlJavaScriptExpression::delayedError() { if (!m_error) m_error = new QQmlDelayedError; - return m_error; + return m_error.data(); } QV4::ReturnedValue diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 9562476940..de3fba0774 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -171,13 +171,17 @@ protected: QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> activeGuards; QForwardFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> permanentGuards; + void setTranslationsCaptured(bool captured) { m_error.setFlagValue(captured); } + bool translationsCaptured() const { return m_error.flag(); } + private: friend class QQmlContextData; friend class QQmlPropertyCapture; friend void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *, void **); friend class QQmlTranslationBinding; - QQmlDelayedError *m_error; + // m_error:flag1 translationsCapturedDuringEvaluation + QFlagPointer<QQmlDelayedError> m_error; QQmlContextData *m_context; QQmlJavaScriptExpression **m_prevExpression; @@ -208,12 +212,14 @@ public: static void registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction); void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce); void captureProperty(QObject *, int, int, Duration duration = OnlyOnce, bool doNotify = true); + void captureTranslation() { translationCaptured = true; } QQmlEngine *engine; QQmlJavaScriptExpression *expression; QQmlJavaScriptExpression::DeleteWatcher *watcher; QFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> guards; QStringList *errorString; + bool translationCaptured = false; }; QQmlJavaScriptExpression::DeleteWatcher::DeleteWatcher(QQmlJavaScriptExpression *e) @@ -260,18 +266,17 @@ void QQmlJavaScriptExpression::setScopeObject(QObject *v) bool QQmlJavaScriptExpression::hasError() const { - return m_error && m_error->isValid(); + return !m_error.isNull() && m_error->isValid(); } bool QQmlJavaScriptExpression::hasDelayedError() const { - return m_error; + return !m_error.isNull(); } inline void QQmlJavaScriptExpression::clearError() { - if (m_error) - delete m_error; + delete m_error.data(); m_error = nullptr; } diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 60617630d2..bec9c9840f 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1344,6 +1344,11 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru data->clearPendingBindingBit(b->targetPropertyIndex().coreIndex()); b->setEnabled(true, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); + if (!b->isValueTypeProxy()) { + QQmlBinding *binding = static_cast<QQmlBinding*>(b.data()); + if (!binding->hasError() && !binding->hasDependencies()) + b->removeFromObject(); + } if (watcher.hasRecursed() || interrupt.shouldInterrupt()) return nullptr; diff --git a/src/qml/qml/qqmlvaluetypeproxybinding_p.h b/src/qml/qml/qqmlvaluetypeproxybinding_p.h index ba0d305bd9..35b54c339b 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding_p.h +++ b/src/qml/qml/qqmlvaluetypeproxybinding_p.h @@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE -class QQmlValueTypeProxyBinding : public QQmlAbstractBinding +class Q_AUTOTEST_EXPORT QQmlValueTypeProxyBinding : public QQmlAbstractBinding { public: QQmlValueTypeProxyBinding(QObject *o, QQmlPropertyIndex coreIndex); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 571f0af9d4..4ae78645ea 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1853,6 +1853,10 @@ ReturnedValue GlobalExtensions::method_qsTranslate(const FunctionObject *b, cons if (argc > i) n = argv[i].toInt32(); + if (QQmlEnginePrivate *ep = (scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr)) + if (ep->propertyCapture) + ep->propertyCapture->captureTranslation(); + QString result = QCoreApplication::translate(context.toUtf8().constData(), text.toUtf8().constData(), comment.toUtf8().constData(), @@ -1956,6 +1960,10 @@ ReturnedValue GlobalExtensions::method_qsTr(const FunctionObject *b, const Value if (argc > 2) n = argv[2].toInt32(); + if (QQmlEnginePrivate *ep = (scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr)) + if (ep->propertyCapture) + ep->propertyCapture->captureTranslation(); + QString result = QCoreApplication::translate(context.toUtf8().constData(), text.toUtf8().constData(), comment.toUtf8().constData(), n); @@ -2036,6 +2044,10 @@ ReturnedValue GlobalExtensions::method_qsTrId(const FunctionObject *b, const Val if (argc > 1) n = argv[1].toInt32(); + if (QQmlEnginePrivate *ep = (scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr)) + if (ep->propertyCapture) + ep->propertyCapture->captureTranslation(); + return Encode(scope.engine->newString(qtTrId(argv[0].toQStringNoThrow().toUtf8().constData(), n))); } |