diff options
author | Ulf Hermann <[email protected]> | 2022-05-20 15:26:47 +0200 |
---|---|---|
committer | Ulf Hermann <[email protected]> | 2022-05-24 15:44:16 +0200 |
commit | dc77f7c71c532a1c51f7618765559937fd5d2eb3 (patch) | |
tree | f1d633a5033b0a4cc30c52aac9b3b0a5be05385d | |
parent | 3d8702378ba7f8252862aac8d71995d61f4f2b69 (diff) |
Replace synthetic AOT functions with property-to-property bindings
Those should be more efficient and make your feet attract fewer
projectiles.
Fixes: QTBUG-103588
Change-Id: I8b25b9edb1edf5e112dbcba5bba898646d29ae2b
Reviewed-by: Fabian Kosmale <[email protected]>
-rw-r--r-- | src/qml/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4function_p.h | 14 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 5 | ||||
-rw-r--r-- | src/qml/qml/qqmlabstractbinding.cpp | 96 | ||||
-rw-r--r-- | src/qml/qml/qqmlabstractbinding_p.h | 40 | ||||
-rw-r--r-- | src/qml/qml/qqmlanybinding_p.h | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlbinding.cpp | 86 | ||||
-rw-r--r-- | src/qml/qml/qqmlbinding_p.h | 33 | ||||
-rw-r--r-- | src/qml/qml/qqmljavascriptexpression.cpp | 9 | ||||
-rw-r--r-- | src/qml/qml/qqmljavascriptexpression_p.h | 5 | ||||
-rw-r--r-- | src/qml/qml/qqmlnotifier.cpp | 4 | ||||
-rw-r--r-- | src/qml/qml/qqmlnotifier_p.h | 3 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertytopropertybinding.cpp | 164 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertytopropertybinding_p.h | 100 | ||||
-rw-r--r-- | src/qml/qml/qqmlvaluetypewrapper.cpp | 2 | ||||
-rw-r--r-- | src/qmlmodels/qqmldelegatemodel.cpp | 34 | ||||
-rw-r--r-- | src/quick/util/qquickstategroup.cpp | 2 |
19 files changed, 417 insertions, 187 deletions
diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt index af9beeb627..a10228f3bb 100644 --- a/src/qml/CMakeLists.txt +++ b/src/qml/CMakeLists.txt @@ -312,6 +312,7 @@ qt_internal_add_qml_module(Qml qml/qqmlpropertydata_p.h qml/qqmlpropertyindex_p.h qml/qqmlpropertyresolver.cpp qml/qqmlpropertyresolver_p.h + qml/qqmlpropertytopropertybinding.cpp qml/qqmlpropertytopropertybinding_p.h qml/qqmlpropertyvalidator.cpp qml/qqmlpropertyvalidator_p.h qml/qqmlpropertyvalueinterceptor.cpp qml/qqmlpropertyvalueinterceptor_p.h qml/qqmlfinalizer.cpp qml/qqmlfinalizer_p.h diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 8f2a38416d..849cb691fc 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -127,8 +127,6 @@ public: const QQmlPrivate::AOTCompiledFunction *aotFunction); void destroy(); - bool isSyntheticAotFunction() const { return codeData == nullptr && aotFunction != nullptr; } - // used when dynamically assigning signal handlers (QQmlConnection) void updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters); @@ -156,18 +154,6 @@ public: } }; -struct SyntheticAotFunction : public Function -{ - SyntheticAotFunction(ExecutionEngine *engine, QQmlPrivate::AOTCompiledFunction aotFunction) - : Function(engine, &m_aotFunction) - , m_aotFunction(std::move(aotFunction)) - { - } - -private: - QQmlPrivate::AOTCompiledFunction m_aotFunction; -}; - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 0a849e8eb3..3d5371b7e0 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -555,7 +555,7 @@ void QObjectWrapper::setProperty( if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) { if (auto binding = QQmlPropertyPrivate::binding(object, QQmlPropertyIndex(property->coreIndex()))) { - Q_ASSERT(binding->kind() == QQmlAbstractBinding::Binding); + Q_ASSERT(binding->kind() == QQmlAbstractBinding::QmlBinding); const auto qmlBinding = static_cast<const QQmlBinding*>(binding); const auto stackFrame = engine->currentStackFrame; qCInfo(lcBindingRemoval, diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 5f41b4b601..e8d4f0a09c 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -494,10 +494,7 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine) } aotContext.engine = engine->jsEngine(); - if (function->isSyntheticAotFunction()) - aotContext.extraData = function->aotFunction->extraData; - else - aotContext.compilationUnit = function->executableCompilationUnit(); + aotContext.compilationUnit = function->executableCompilationUnit(); function->aotFunction->functionPtr( &aotContext, transformedResult ? transformedResult : frame->returnValue(), diff --git a/src/qml/qml/qqmlabstractbinding.cpp b/src/qml/qml/qqmlabstractbinding.cpp index 8c28c525f2..a84607d035 100644 --- a/src/qml/qml/qqmlabstractbinding.cpp +++ b/src/qml/qml/qqmlabstractbinding.cpp @@ -42,6 +42,7 @@ #include <QtQml/qqmlinfo.h> #include <private/qqmlbinding_p.h> #include <private/qqmlvaluetypeproxybinding_p.h> +#include <private/qqmlvmemetaobject_p.h> QT_BEGIN_NAMESPACE @@ -190,11 +191,104 @@ void QQmlAbstractBinding::removeFromObject() data->clearBindingBit(coreIndex); } -void QQmlAbstractBinding::printBindingLoopError(QQmlProperty &prop) +void QQmlAbstractBinding::printBindingLoopError(const QQmlProperty &prop) { qmlWarning(prop.object()) << QString(QLatin1String("Binding loop detected for property \"%1\"")).arg(prop.name()); } +void QQmlAbstractBinding::getPropertyData( + const QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const +{ + Q_ASSERT(propertyData); + + QQmlData *data = QQmlData::get(m_target.data(), false); + Q_ASSERT(data); + + if (Q_UNLIKELY(!data->propertyCache)) + data->propertyCache = QQmlMetaType::propertyCache(m_target->metaObject()); + + *propertyData = data->propertyCache->property(m_targetIndex.coreIndex()); + Q_ASSERT(*propertyData); + + if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex() && valueTypeData)) { + const QMetaObject *valueTypeMetaObject + = QQmlMetaType::metaObjectForValueType((*propertyData)->propType()); + Q_ASSERT(valueTypeMetaObject); + QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex()); + valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp)); + valueTypeData->setPropType(vtProp.metaType()); + valueTypeData->setCoreIndex(m_targetIndex.valueTypeIndex()); + } +} + +void QQmlAbstractBinding::updateCanUseAccessor() +{ + setCanUseAccessor(true); // Always use accessors, except when: + if (auto interceptorMetaObject = QQmlInterceptorMetaObject::get(targetObject())) { + if (!m_targetIndex.isValid() || interceptorMetaObject->intercepts(m_targetIndex)) + setCanUseAccessor(false); + } +} + +void QQmlAbstractBinding::setTarget(const QQmlProperty &prop) +{ + auto pd = QQmlPropertyPrivate::get(prop); + setTarget(prop.object(), pd->core, &pd->valueTypeData); +} + +bool QQmlAbstractBinding::setTarget( + QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType) +{ + return setTarget(object, core.coreIndex(), core.isAlias(), + valueType ? valueType->coreIndex() : -1); +} + +bool QQmlAbstractBinding::setTarget( + QObject *object, int coreIndex, bool coreIsAlias, int valueTypeIndex) +{ + m_target = object; + + if (!object) { + m_targetIndex = QQmlPropertyIndex(); + return false; + } + + for (bool isAlias = coreIsAlias; isAlias;) { + QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); + + int aValueTypeIndex; + if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) { + // can't resolve id (yet) + m_target = nullptr; + m_targetIndex = QQmlPropertyIndex(); + return false; + } + if (valueTypeIndex == -1) + valueTypeIndex = aValueTypeIndex; + + QQmlData *data = QQmlData::get(object, false); + if (!data || !data->propertyCache) { + m_target = nullptr; + m_targetIndex = QQmlPropertyIndex(); + return false; + } + const QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); + Q_ASSERT(propertyData); + + m_target = object; + isAlias = propertyData->isAlias(); + coreIndex = propertyData->coreIndex(); + } + m_targetIndex = QQmlPropertyIndex(coreIndex, valueTypeIndex); + + QQmlData *data = QQmlData::get(m_target.data(), true); + if (!data->propertyCache) + data->propertyCache = QQmlMetaType::propertyCache(m_target->metaObject()); + + return true; +} + + QString QQmlAbstractBinding::expression() const { return QLatin1String("<Unknown>"); diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h index 0f906ebb1f..d159d18d7e 100644 --- a/src/qml/qml/qqmlabstractbinding_p.h +++ b/src/qml/qml/qqmlabstractbinding_p.h @@ -67,7 +67,8 @@ protected: public: enum Kind { ValueTypeProxy, - Binding, + QmlBinding, + PropertyToPropertyBinding, }; virtual ~QQmlAbstractBinding(); @@ -87,17 +88,23 @@ public: // binding is not enabled or added to the object. QObject *targetObject() const { return m_target.data(); } + void setTarget(const QQmlProperty &); + bool setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType); + bool setTarget(QObject *, int coreIndex, bool coreIsAlias, int valueTypeIndex); + virtual void setEnabled(bool e, QQmlPropertyData::WriteFlags f = QQmlPropertyData::DontRemoveBinding) = 0; void addToObject(); void removeFromObject(); - static void printBindingLoopError(QQmlProperty &prop); + static void printBindingLoopError(const QQmlProperty &prop); inline QQmlAbstractBinding *nextBinding() const; inline bool canUseAccessor() const { return m_nextBinding.tag().testFlag(CanUseAccessor); } + void setCanUseAccessor(bool canUseAccessor) + { m_nextBinding.setTag(m_nextBinding.tag().setFlag(CanUseAccessor, canUseAccessor)); } struct RefCount { RefCount() {} @@ -132,6 +139,15 @@ protected: inline void setNextBinding(QQmlAbstractBinding *); + void getPropertyData( + const QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const; + + inline bool updatingFlag() const; + inline void setUpdatingFlag(bool); + inline bool enabledFlag() const; + inline void setEnabledFlag(bool); + void updateCanUseAccessor(); + QQmlPropertyIndex m_targetIndex; // Pointer is the target object to which the binding binds @@ -168,6 +184,26 @@ void QQmlAbstractBinding::setNextBinding(QQmlAbstractBinding *b) m_nextBinding = b; } +bool QQmlAbstractBinding::updatingFlag() const +{ + return m_target.tag().testFlag(UpdatingBinding); +} + +void QQmlAbstractBinding::setUpdatingFlag(bool v) +{ + m_target.setTag(m_target.tag().setFlag(UpdatingBinding, v)); +} + +bool QQmlAbstractBinding::enabledFlag() const +{ + return m_target.tag().testFlag(BindingEnabled); +} + +void QQmlAbstractBinding::setEnabledFlag(bool v) +{ + m_target.setTag(m_target.tag().setFlag(BindingEnabled, v)); +} + QT_END_NAMESPACE #endif // QQMLABSTRACTBINDING_P_H diff --git a/src/qml/qml/qqmlanybinding_p.h b/src/qml/qml/qqmlanybinding_p.h index 00b5980000..d2c12b7f3c 100644 --- a/src/qml/qml/qqmlanybinding_p.h +++ b/src/qml/qml/qqmlanybinding_p.h @@ -273,7 +273,7 @@ public: bool hasError() { if (isAbstractPropertyBinding()) { auto abstractBinding = asAbstractBinding(); - if (abstractBinding->kind() != QQmlAbstractBinding::Binding) + if (abstractBinding->kind() != QQmlAbstractBinding::QmlBinding) return false; return static_cast<QQmlBinding *>(abstractBinding)->hasError(); } else { diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index c0657f7380..0870a5d7d3 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -665,12 +665,7 @@ void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) const bool wasEnabled = enabledFlag(); setEnabledFlag(e); setNotifyOnValueChanged(e); - - m_nextBinding.setTag(m_nextBinding.tag().setFlag(CanUseAccessor)); // Always use accessors, only not when: - if (auto interceptorMetaObject = QQmlInterceptorMetaObject::get(targetObject())) { - if (!m_targetIndex.isValid() || interceptorMetaObject->intercepts(m_targetIndex)) - m_nextBinding.setTag(m_nextBinding.tag().setFlag(CanUseAccessor, false)); - } + updateCanUseAccessor(); if (e && !wasEnabled) update(flags); @@ -681,85 +676,6 @@ QString QQmlBinding::expression() const return QStringLiteral("function() { [native code] }"); } -void QQmlBinding::setTarget(const QQmlProperty &prop) -{ - auto pd = QQmlPropertyPrivate::get(prop); - setTarget(prop.object(), pd->core, &pd->valueTypeData); -} - -bool QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType) -{ - return setTarget(object, core.coreIndex(), core.isAlias(), - valueType ? valueType->coreIndex() : -1); -} - -bool QQmlBinding::setTarget(QObject *object, int coreIndex, bool coreIsAlias, int valueTypeIndex) -{ - m_target = object; - - if (!object) { - m_targetIndex = QQmlPropertyIndex(); - return false; - } - - for (bool isAlias = coreIsAlias; isAlias;) { - QQmlVMEMetaObject *vme = QQmlVMEMetaObject::getForProperty(object, coreIndex); - - int aValueTypeIndex; - if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) { - // can't resolve id (yet) - m_target = nullptr; - m_targetIndex = QQmlPropertyIndex(); - return false; - } - if (valueTypeIndex == -1) - valueTypeIndex = aValueTypeIndex; - - QQmlData *data = QQmlData::get(object, false); - if (!data || !data->propertyCache) { - m_target = nullptr; - m_targetIndex = QQmlPropertyIndex(); - return false; - } - const QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); - Q_ASSERT(propertyData); - - m_target = object; - isAlias = propertyData->isAlias(); - coreIndex = propertyData->coreIndex(); - } - m_targetIndex = QQmlPropertyIndex(coreIndex, valueTypeIndex); - - QQmlData *data = QQmlData::get(m_target.data(), true); - if (!data->propertyCache) - data->propertyCache = QQmlMetaType::propertyCache(m_target->metaObject()); - - return true; -} - -void QQmlBinding::getPropertyData(const QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const -{ - Q_ASSERT(propertyData); - - QQmlData *data = QQmlData::get(m_target.data(), false); - Q_ASSERT(data); - - if (Q_UNLIKELY(!data->propertyCache)) - data->propertyCache = QQmlMetaType::propertyCache(m_target->metaObject()); - - *propertyData = data->propertyCache->property(m_targetIndex.coreIndex()); - Q_ASSERT(*propertyData); - - if (Q_UNLIKELY(m_targetIndex.hasValueTypeIndex() && valueTypeData)) { - const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType((*propertyData)->propType()); - Q_ASSERT(valueTypeMetaObject); - QMetaProperty vtProp = valueTypeMetaObject->property(m_targetIndex.valueTypeIndex()); - valueTypeData->setFlags(QQmlPropertyData::flagsForProperty(vtProp)); - valueTypeData->setPropType(vtProp.metaType()); - valueTypeData->setCoreIndex(m_targetIndex.valueTypeIndex()); - } -} - QVector<QQmlProperty> QQmlBinding::dependencies() const { QVector<QQmlProperty> dependencies; diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index f686a1745b..a83916bc49 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -95,16 +95,11 @@ public: const QV4::CompiledData::Binding *binding, QObject *obj, const QQmlRefPointer<QQmlContextData> &ctxt); - Kind kind() const final { return QQmlAbstractBinding::Binding; } + Kind kind() const final { return QQmlAbstractBinding::QmlBinding; } ~QQmlBinding() override; bool mustCaptureBindableProperty() const final {return true;} - - void setTarget(const QQmlProperty &); - bool setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType); - bool setTarget(QObject *, int coreIndex, bool coreIsAlias, int valueTypeIndex); - void refresh() override; void setEnabled(bool, QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding) override; @@ -142,7 +137,6 @@ protected: virtual void doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) = 0; - void getPropertyData(const QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const; int getPropertyType() const; bool slowWrite(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData, @@ -158,11 +152,6 @@ protected: } private: - inline bool updatingFlag() const; - inline void setUpdatingFlag(bool); - inline bool enabledFlag() const; - inline void setEnabledFlag(bool); - static QQmlBinding *newBinding(const QQmlPropertyData *property); static QQmlBinding *newBinding(QMetaType propertyType); @@ -171,26 +160,6 @@ private: void handleWriteError(const void *result, QMetaType resultType, QMetaType metaType); }; -bool QQmlBinding::updatingFlag() const -{ - return m_target.tag().testFlag(UpdatingBinding); -} - -void QQmlBinding::setUpdatingFlag(bool v) -{ - m_target.setTag(m_target.tag().setFlag(UpdatingBinding, v)); -} - -bool QQmlBinding::enabledFlag() const -{ - return m_target.tag().testFlag(BindingEnabled); -} - -void QQmlBinding::setEnabledFlag(bool v) -{ - m_target.setTag(m_target.tag().setFlag(BindingEnabled, v)); -} - QT_END_NAMESPACE Q_DECLARE_METATYPE(QQmlBinding*) diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 1ef60f5350..9ebaa0bba5 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -123,9 +123,6 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() clearError(); if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion. m_scopeObject.asT2()->_s = nullptr; - - if (m_v4Function.tag() == OwnsSyntheticAotFunction) - delete static_cast<QV4::SyntheticAotFunction *>(m_v4Function.data()); } QString QQmlJavaScriptExpression::expressionIdentifier() const @@ -539,12 +536,6 @@ void QQmlJavaScriptExpression::setupFunction(QV4::ExecutionContext *qmlContext, return; m_qmlScope.set(qmlContext->engine(), *qmlContext); m_v4Function = f; - - // Synthetic AOT functions are owned by the QQmlJavaScriptExpressions they are assigned to. - // We need to check this here, because non-synthetic functions may be removed before the - // QQmlJavaScriptExpressions that use them. - m_v4Function.setTag(f->isSyntheticAotFunction() ? OwnsSyntheticAotFunction : DoesNotOwn); - m_compilationUnit.reset(m_v4Function->executableCompilationUnit()); } diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index b57b3982ec..273f6b3edb 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -138,7 +138,7 @@ public: *listHead = this; } - QV4::Function *function() const { return m_v4Function.data(); } + QV4::Function *function() const { return m_v4Function; } virtual void refresh(); @@ -212,8 +212,7 @@ private: QV4::PersistentValue m_qmlScope; QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compilationUnit; - enum Ownership { DoesNotOwn, OwnsSyntheticAotFunction }; - QTaggedPointer<QV4::Function, Ownership> m_v4Function; + QV4::Function *m_v4Function; protected: TriggerList *qpropertyChangeTriggers = nullptr; diff --git a/src/qml/qml/qqmlnotifier.cpp b/src/qml/qml/qqmlnotifier.cpp index df3731684a..6e1a0881aa 100644 --- a/src/qml/qml/qqmlnotifier.cpp +++ b/src/qml/qml/qqmlnotifier.cpp @@ -49,12 +49,14 @@ typedef void (*Callback)(QQmlNotifierEndpoint *, void **); void QQmlBoundSignal_callback(QQmlNotifierEndpoint *, void **); void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *, void **); void QQmlVMEMetaObjectEndpoint_callback(QQmlNotifierEndpoint *, void **); +void QQmlPropertyGuard_callback(QQmlNotifierEndpoint *, void **); static Callback QQmlNotifier_callbacks[] = { nullptr, QQmlBoundSignal_callback, QQmlJavaScriptExpressionGuard_callback, - QQmlVMEMetaObjectEndpoint_callback + QQmlVMEMetaObjectEndpoint_callback, + QQmlPropertyGuard_callback }; namespace { diff --git a/src/qml/qml/qqmlnotifier_p.h b/src/qml/qml/qqmlnotifier_p.h index cfb399b3b5..4ac144276e 100644 --- a/src/qml/qml/qqmlnotifier_p.h +++ b/src/qml/qml/qqmlnotifier_p.h @@ -91,7 +91,8 @@ public: None = 0, QQmlBoundSignal = 1, QQmlJavaScriptExpressionGuard = 2, - QQmlVMEMetaObjectEndpoint = 3 + QQmlVMEMetaObjectEndpoint = 3, + QQmlPropertyGuard = 4, }; inline QQmlNotifierEndpoint(Callback callback); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 052cf62b20..0fa0119dac 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1426,7 +1426,7 @@ bool QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt) data->clearPendingBindingBit(b->targetPropertyIndex().coreIndex()); b->setEnabled(true, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding); - if (b->kind() == QQmlAbstractBinding::Binding) { + if (b->kind() == QQmlAbstractBinding::QmlBinding) { QQmlBinding *binding = static_cast<QQmlBinding*>(b.data()); if (!binding->hasError() && !binding->hasDependencies() && !binding->hasUnresolvedNames()) { diff --git a/src/qml/qml/qqmlpropertytopropertybinding.cpp b/src/qml/qml/qqmlpropertytopropertybinding.cpp new file mode 100644 index 0000000000..001d3e8286 --- /dev/null +++ b/src/qml/qml/qqmlpropertytopropertybinding.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlpropertytopropertybinding_p.h" +#include <private/qqmlvmemetaobject_p.h> + +QT_BEGIN_NAMESPACE + +/*! + * \internal + * \class QQmlPropertyToPropertyBinding + * + * This class can be used to create a direct binding from a source property to + * a target property, without going through QQmlJavaScriptExpression and + * QV4::Function. In particular you don't need a compilation unit or byte code + * to set this up. + */ + +QQmlPropertyToPropertyBinding::QQmlPropertyToPropertyBinding( + QQmlEngine *engine, QObject *sourceObject, int sourcePropertyIndex, + QObject *targetObject, int targetPropertyIndex) + : QQmlNotifierEndpoint(QQmlPropertyGuard) + , m_engine(engine) + , m_sourceObject(sourceObject) + , m_sourcePropertyIndex(sourcePropertyIndex) +{ + setTarget(targetObject, targetPropertyIndex, false, -1); +} + +QQmlAbstractBinding::Kind QQmlPropertyToPropertyBinding::kind() const +{ + return PropertyToPropertyBinding; +} + +void QQmlPropertyToPropertyBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) +{ + const bool wasEnabled = enabledFlag(); + setEnabledFlag(e); + updateCanUseAccessor(); + if (e && !wasEnabled) + update(flags); +} + +void QQmlPropertyToPropertyBinding::captureProperty( + const QMetaObject *sourceMetaObject, int notifyIndex, + bool isSourceBindable, bool isTargetBindable) +{ + if (isSourceBindable) { + // if the property is a QPropery, and we're binding to a QProperty + // the automatic capturing process already takes care of everything + if (isTargetBindable) + return; + + // We have already captured. + if (observer) + return; + + observer = std::make_unique<Observer>(this); + QUntypedBindable bindable; + void *argv[] = { &bindable }; + sourceMetaObject->metacall( + m_sourceObject, QMetaObject::BindableProperty, m_sourcePropertyIndex, argv); + bindable.observe(observer.get()); + return; + } + + // We cannot capture non-bindable properties without signals + if (notifyIndex == -1) + return; + + if (isConnected(m_sourceObject, notifyIndex)) + cancelNotify(); + else + connect(m_sourceObject, notifyIndex, m_engine, true); +} + +void QQmlPropertyToPropertyBinding::update(QQmlPropertyData::WriteFlags flags) +{ + if (!enabledFlag()) + return; + + // Check that the target has not been deleted + QObject *target = targetObject(); + if (QQmlData::wasDeleted(target)) + return; + + const QQmlPropertyData *d = nullptr; + QQmlPropertyData vtd; + getPropertyData(&d, &vtd); + Q_ASSERT(d); + + // Check for a binding update loop + if (Q_UNLIKELY(updatingFlag())) { + QQmlAbstractBinding::printBindingLoopError( + QQmlPropertyPrivate::restore(target, *d, &vtd, nullptr)); + return; + } + + setUpdatingFlag(true); + + if (canUseAccessor()) + flags.setFlag(QQmlPropertyData::BypassInterceptor); + + const QMetaObject *sourceMetaObject = m_sourceObject->metaObject(); + const QMetaProperty property = sourceMetaObject->property(m_sourcePropertyIndex); + if (!property.isConstant()) { + captureProperty(sourceMetaObject, QMetaObjectPrivate::signalIndex(property.notifySignal()), + property.isBindable(), !vtd.isValid() && d->isBindable()); + } + + QQmlPropertyPrivate::writeValueProperty( + target, *d, vtd, property.read(m_sourceObject), {}, flags); + + setUpdatingFlag(false); +} + +void QQmlPropertyGuard_callback(QQmlNotifierEndpoint *e, void **) +{ + static_cast<QQmlPropertyToPropertyBinding *>(e)->update(); +} + +void QQmlPropertyToPropertyBinding::Observer::trigger( + QPropertyObserver *observer, QUntypedPropertyData *) +{ + static_cast<Observer *>(observer)->binding->update(); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlpropertytopropertybinding_p.h b/src/qml/qml/qqmlpropertytopropertybinding_p.h new file mode 100644 index 0000000000..b75f97794b --- /dev/null +++ b/src/qml/qml/qqmlpropertytopropertybinding_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROPERTYTOPROPERTYBINDINDING_P_H +#define QQMLPROPERTYTOPROPERTYBINDINDING_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 <private/qqmlabstractbinding_p.h> +#include <private/qqmlnotifier_p.h> +#include <QtCore/qproperty.h> + +QT_BEGIN_NAMESPACE + +class Q_QML_PRIVATE_EXPORT QQmlPropertyToPropertyBinding + : public QQmlAbstractBinding, public QQmlNotifierEndpoint +{ +public: + QQmlPropertyToPropertyBinding( + QQmlEngine *engine, QObject *sourceObject, int sourcePropertyIndex, + QObject *targetObject, int targetPropertyIndex); + + Kind kind() const final; + void setEnabled(bool e, QQmlPropertyData::WriteFlags flags) final; + + void update(QQmlPropertyData::WriteFlags flags = QQmlPropertyData::DontRemoveBinding); + +private: + static void trigger(QPropertyObserver *, QUntypedPropertyData *); + + void captureProperty( + const QMetaObject *sourceMetaObject, int notifyIndex, + bool isSourceBindable, bool isTargetBindable); + + struct Observer : QPropertyObserver { + static void trigger(QPropertyObserver *observer, QUntypedPropertyData *); + Observer(QQmlPropertyToPropertyBinding *binding) + : QPropertyObserver(trigger) + , binding(binding) + { + } + QQmlPropertyToPropertyBinding *binding = nullptr; + }; + + std::unique_ptr<Observer> observer; + QQmlEngine *m_engine = nullptr; + QObject *m_sourceObject = nullptr; + int m_sourcePropertyIndex = -1; +}; + +void QQmlPropertyGuard_callback(QQmlNotifierEndpoint *e, void **); + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 20d418744d..7e9e688f50 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -637,7 +637,7 @@ bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &v } else { if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) { if (auto binding = QQmlPropertyPrivate::binding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd.coreIndex()))) { - Q_ASSERT(binding->kind() == QQmlAbstractBinding::Binding); + Q_ASSERT(binding->kind() == QQmlAbstractBinding::QmlBinding); const auto qmlBinding = static_cast<const QQmlBinding*>(binding); const auto stackFrame = v4->currentStackFrame; qCInfo(lcBindingRemoval, diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp index 5080fad612..3eb6ea433e 100644 --- a/src/qmlmodels/qqmldelegatemodel.cpp +++ b/src/qmlmodels/qqmldelegatemodel.cpp @@ -49,6 +49,7 @@ #include <private/qqmlchangeset_p.h> #include <private/qqmlengine_p.h> #include <private/qqmlcomponent_p.h> +#include <private/qqmlpropertytopropertybinding_p.h> #include <private/qjsvalue_p.h> #include <private/qv4value_p.h> @@ -940,24 +941,6 @@ static bool isDoneIncubating(QQmlIncubator::Status status) return status == QQmlIncubator::Ready || status == QQmlIncubator::Error; } -static void bindingFunction( - const QQmlPrivate::AOTCompiledContext *context, void *resultPtr, void **) -{ - // metaCall expects initialized memory, the AOT function passes uninitialized memory. - QObject *scopeObject = context->qmlScopeObject; - const int propertyIndex = context->extraData; - const QMetaObject *metaObject = scopeObject->metaObject(); - const QMetaProperty property = metaObject->property(propertyIndex); - property.metaType().construct(resultPtr); - - context->qmlEngine()->captureProperty(scopeObject, property); - - int status = -1; - int flags = 0; - void *argv[] = { resultPtr, nullptr, &status, &flags }; - metaObject->metacall(scopeObject, QMetaObject::ReadProperty, propertyIndex, argv); -} - void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *modelItemToIncubate, QObject *object) { auto incubatorPriv = QQmlIncubatorPrivate::get(this); @@ -1022,18 +1005,9 @@ void QQDMIncubationTask::initializeRequiredProperties(QQmlDelegateModelItem *mod object, propName, requiredProperties, engine, &wasInRequired); if (wasInRequired) { - QV4::SyntheticAotFunction *function = new QV4::SyntheticAotFunction( - engine->handle(), QQmlPrivate::AOTCompiledFunction { - i, prop.metaType(), {}, bindingFunction - }); - - if (!qmlContext) { - qmlContext = QV4::QmlContext::create( - v4->rootContext(), contextData, itemOrProxy); - } - - QQmlAnyBinding binding = QQmlAnyBinding::createFromFunction( - targetProp, function, itemOrProxy, contextData, qmlContext); + QQmlAnyBinding binding; + binding = new QQmlPropertyToPropertyBinding( + engine, itemOrProxy, i, targetProp.object(), targetProp.index()); binding.installOn(targetProp); } } diff --git a/src/quick/util/qquickstategroup.cpp b/src/quick/util/qquickstategroup.cpp index e9a21b7c60..82b15cdf04 100644 --- a/src/quick/util/qquickstategroup.cpp +++ b/src/quick/util/qquickstategroup.cpp @@ -386,7 +386,7 @@ bool QQuickStateGroupPrivate::updateAutoState() // if there is a binding, the value in when might not be up-to-date at this point // so we manually re-evaluate the binding QQmlAbstractBinding *abstractBinding = potentialWhenBinding.asAbstractBinding(); - if (abstractBinding && abstractBinding->kind() == QQmlAbstractBinding::Binding) { + if (abstractBinding && abstractBinding->kind() == QQmlAbstractBinding::QmlBinding) { QQmlBinding *binding = static_cast<QQmlBinding *>(abstractBinding); if (binding->hasValidContext()) whenValue = binding->evaluate().toBool(); |