aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2022-01-05 12:37:33 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2022-01-18 21:07:33 +0000
commit1269e29a0f37190e72ba19b96769ae0d12290808 (patch)
tree24ee1fdc7b20e34388edb34c86946b4621e6b386 /src/qml/qml
parentb8e267c074b0537a8c7754e63004dcd2643f1e10 (diff)
Inline the pre-checks for QQmlInterceptorMetaObject::intercept
In most cases we won't intercept. Calling a function just to figure out that there are no interceptors or that the call type doesn't match is expensive. Also, better encapsulate the class and initialize members inline where possible. Change-Id: Id545c98bc52690c75014b1283edb72a241750960 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> (cherry picked from commit 7b00dd860728fa078dac78359811b7c718d5e20f) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
Diffstat (limited to 'src/qml/qml')
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp156
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h31
2 files changed, 100 insertions, 87 deletions
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 0cef04ab89..a86c249e40 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -272,9 +272,7 @@ void QQmlVMEMetaObjectEndpoint::tryConnect()
QQmlInterceptorMetaObject::QQmlInterceptorMetaObject(QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache)
: object(obj),
- cache(cache),
- interceptors(nullptr),
- metaObject(nullptr)
+ cache(cache)
{
QObjectPrivate *op = QObjectPrivate::get(obj);
@@ -312,90 +310,86 @@ int QQmlInterceptorMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id,
return object->qt_metacall(c, id, a);
}
-bool QQmlInterceptorMetaObject::intercept(QMetaObject::Call c, int id, void **a)
+bool QQmlInterceptorMetaObject::doIntercept(QMetaObject::Call c, int id, void **a)
{
- if ( ( (c == QMetaObject::WriteProperty &&
- !(*reinterpret_cast<int*>(a[3]) & QQmlPropertyData::BypassInterceptor)) || c == QMetaObject::BindableProperty )
- && interceptors ) {
-
- for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) {
- if (vi->m_propertyIndex.coreIndex() != id)
- continue;
-
- const int valueIndex = vi->m_propertyIndex.valueTypeIndex();
- const QQmlData *data = QQmlData::get(object);
- const QMetaType metaType = data->propertyCache->property(id)->propType();
-
- if (metaType.isValid()) {
- if (valueIndex != -1 && c == QMetaObject::WriteProperty) {
- // TODO: handle intercepting bindable properties for value types?
- QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance(
- data->context->engine(), metaType);
- Q_ASSERT(valueType);
-
- //
- // Consider the following case:
- // color c = { 0.1, 0.2, 0.3 }
- // interceptor exists on c.r
- // write { 0.2, 0.4, 0.6 }
- //
- // The interceptor may choose not to update the r component at this
- // point (for example, a behavior that creates an animation). But we
- // need to ensure that the g and b components are updated correctly.
- //
- // So we need to perform a full write where the value type is:
- // r = old value, g = new value, b = new value
- //
- // And then call the interceptor which may or may not write the
- // new value to the r component.
- //
- // This will ensure that the other components don't contain stale data
- // and any relevant signals are emitted.
- //
- // To achieve this:
- // (1) Store the new value type as a whole (needed due to
- // aliasing between a[0] and static storage in value type).
- // (2) Read the entire existing value type from object -> valueType temp.
- // (3) Read the previous value of the component being changed
- // from the valueType temp.
- // (4) Write the entire new value type into the temp.
- // (5) Overwrite the component being changed with the old value.
- // (6) Perform a full write to the value type (which may emit signals etc).
- // (7) Issue the interceptor call with the new component value.
- //
-
- QMetaProperty valueProp = valueType->property(valueIndex);
- QVariant newValue(metaType, a[0]);
-
- valueType->read(object, id);
- QVariant prevComponentValue = valueProp.read(valueType);
-
- valueType->setValue(newValue);
- QVariant newComponentValue = valueProp.read(valueType);
-
- // Don't apply the interceptor if the intercepted value has not changed
- bool updated = false;
- if (newComponentValue != prevComponentValue) {
- valueProp.write(valueType, prevComponentValue);
- valueType->write(object, id, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
-
- vi->write(newComponentValue);
- updated = true;
- }
+ for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) {
+ if (vi->m_propertyIndex.coreIndex() != id)
+ continue;
+
+ const int valueIndex = vi->m_propertyIndex.valueTypeIndex();
+ const QQmlData *data = QQmlData::get(object);
+ const QMetaType metaType = data->propertyCache->property(id)->propType();
+
+ if (metaType.isValid()) {
+ if (valueIndex != -1 && c == QMetaObject::WriteProperty) {
+ // TODO: handle intercepting bindable properties for value types?
+ QQmlGadgetPtrWrapper *valueType = QQmlGadgetPtrWrapper::instance(
+ data->context->engine(), metaType);
+ Q_ASSERT(valueType);
+
+ //
+ // Consider the following case:
+ // color c = { 0.1, 0.2, 0.3 }
+ // interceptor exists on c.r
+ // write { 0.2, 0.4, 0.6 }
+ //
+ // The interceptor may choose not to update the r component at this
+ // point (for example, a behavior that creates an animation). But we
+ // need to ensure that the g and b components are updated correctly.
+ //
+ // So we need to perform a full write where the value type is:
+ // r = old value, g = new value, b = new value
+ //
+ // And then call the interceptor which may or may not write the
+ // new value to the r component.
+ //
+ // This will ensure that the other components don't contain stale data
+ // and any relevant signals are emitted.
+ //
+ // To achieve this:
+ // (1) Store the new value type as a whole (needed due to
+ // aliasing between a[0] and static storage in value type).
+ // (2) Read the entire existing value type from object -> valueType temp.
+ // (3) Read the previous value of the component being changed
+ // from the valueType temp.
+ // (4) Write the entire new value type into the temp.
+ // (5) Overwrite the component being changed with the old value.
+ // (6) Perform a full write to the value type (which may emit signals etc).
+ // (7) Issue the interceptor call with the new component value.
+ //
+
+ QMetaProperty valueProp = valueType->property(valueIndex);
+ QVariant newValue(metaType, a[0]);
+
+ valueType->read(object, id);
+ QVariant prevComponentValue = valueProp.read(valueType);
+
+ valueType->setValue(newValue);
+ QVariant newComponentValue = valueProp.read(valueType);
+
+ // Don't apply the interceptor if the intercepted value has not changed
+ bool updated = false;
+ if (newComponentValue != prevComponentValue) {
+ valueProp.write(valueType, prevComponentValue);
+ valueType->write(object, id, QQmlPropertyData::DontRemoveBinding | QQmlPropertyData::BypassInterceptor);
+
+ vi->write(newComponentValue);
+ updated = true;
+ }
- if (updated)
- return true;
- } else if (c == QMetaObject::WriteProperty) {
- vi->write(QVariant(metaType, a[0]));
+ if (updated)
return true;
- } else {
- object->qt_metacall(c, id, a);
- QUntypedBindable target = *reinterpret_cast<QUntypedBindable *>(a[0]);
- return vi->bindable(reinterpret_cast<QUntypedBindable *>(a[0]), target);
- }
+ } else if (c == QMetaObject::WriteProperty) {
+ vi->write(QVariant(metaType, a[0]));
+ return true;
+ } else {
+ object->qt_metacall(c, id, a);
+ QUntypedBindable target = *reinterpret_cast<QUntypedBindable *>(a[0]);
+ return vi->bindable(reinterpret_cast<QUntypedBindable *>(a[0]), target);
}
}
}
+
return false;
}
diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h
index 0a5b8c6785..19aba84258 100644
--- a/src/qml/qml/qqmlvmemetaobject_p.h
+++ b/src/qml/qml/qqmlvmemetaobject_p.h
@@ -115,17 +115,36 @@ public:
return false;
}
+ QObject *object = nullptr;
+ QQmlRefPointer<QQmlPropertyCache> cache;
+
protected:
int metaCall(QObject *o, QMetaObject::Call c, int id, void **a) override;
- bool intercept(QMetaObject::Call c, int id, void **a);
+ bool intercept(QMetaObject::Call c, int id, void **a)
+ {
+ if (!interceptors)
+ return false;
+
+ switch (c) {
+ case QMetaObject::WriteProperty:
+ if (*reinterpret_cast<int*>(a[3]) & QQmlPropertyData::BypassInterceptor)
+ return false;
+ break;
+ case QMetaObject::BindableProperty:
+ break;
+ default:
+ return false;
+ }
+
+ return doIntercept(c, id, a);
+ }
-public:
- QObject *object;
- QQmlRefPointer<QQmlPropertyCache> cache;
QBiPointer<QDynamicMetaObjectData, const QMetaObject> parent;
+ const QMetaObject *metaObject = nullptr;
- QQmlPropertyValueInterceptor *interceptors;
- const QMetaObject *metaObject;
+private:
+ bool doIntercept(QMetaObject::Call c, int id, void **a);
+ QQmlPropertyValueInterceptor *interceptors = nullptr;
};
inline QQmlInterceptorMetaObject *QQmlInterceptorMetaObject::get(QObject *obj)