aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-05-20 15:26:47 +0200
committerUlf Hermann <ulf.hermann@qt.io>2022-05-24 15:44:16 +0200
commitdc77f7c71c532a1c51f7618765559937fd5d2eb3 (patch)
treef1d633a5033b0a4cc30c52aac9b3b0a5be05385d
parent3d8702378ba7f8252862aac8d71995d61f4f2b69 (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 <fabian.kosmale@qt.io>
-rw-r--r--src/qml/CMakeLists.txt1
-rw-r--r--src/qml/jsruntime/qv4function_p.h14
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp2
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp5
-rw-r--r--src/qml/qml/qqmlabstractbinding.cpp96
-rw-r--r--src/qml/qml/qqmlabstractbinding_p.h40
-rw-r--r--src/qml/qml/qqmlanybinding_p.h2
-rw-r--r--src/qml/qml/qqmlbinding.cpp86
-rw-r--r--src/qml/qml/qqmlbinding_p.h33
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp9
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h5
-rw-r--r--src/qml/qml/qqmlnotifier.cpp4
-rw-r--r--src/qml/qml/qqmlnotifier_p.h3
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp2
-rw-r--r--src/qml/qml/qqmlpropertytopropertybinding.cpp164
-rw-r--r--src/qml/qml/qqmlpropertytopropertybinding_p.h100
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp2
-rw-r--r--src/qmlmodels/qqmldelegatemodel.cpp34
-rw-r--r--src/quick/util/qquickstategroup.cpp2
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> &parameters);
@@ -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();