aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlbinding.cpp
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@qt.io>2016-07-14 16:13:05 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2016-08-02 18:31:09 +0000
commitdbf131fa70dc6269349f3b8d1bedd05203a0c3e4 (patch)
treeede02821f6e224c34252d4a37e821171c961f903 /src/qml/qml/qqmlbinding.cpp
parent546679658ed0a39f758a9dcfad8f16a6552da7fa (diff)
QML: Add specialized binding for QObject* properties
In it we can cache the meta-type for the specific QObject subclass of the property type. This reduces the number of calls to QMetaType. Change-Id: Ie3774395c971bc33af58a8290453b19631939ea8 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/qml/qml/qqmlbinding.cpp')
-rw-r--r--src/qml/qml/qqmlbinding.cpp96
1 files changed, 80 insertions, 16 deletions
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 10d16a8a12..ffe1b4287d 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -51,6 +51,8 @@
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
+#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4variantobject_p.h>
#include <QVariant>
#include <QtCore/qdebug.h>
@@ -59,7 +61,7 @@ QT_BEGIN_NAMESPACE
QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContext *ctxt)
{
- QQmlBinding *b = newBinding(property);
+ QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
b->setNotifyOnValueChanged(true);
b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt));
b->setScopeObject(obj);
@@ -71,7 +73,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString
QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScriptString &script, QObject *obj, QQmlContext *ctxt)
{
- QQmlBinding *b = newBinding(property);
+ QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
if (ctxt && !ctxt->isValid())
return b;
@@ -108,7 +110,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr
QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContextData *ctxt)
{
- QQmlBinding *b = newBinding(property);
+ QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
b->setNotifyOnValueChanged(true);
b->QQmlJavaScriptExpression::setContext(ctxt);
@@ -123,7 +125,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString
QQmlContextData *ctxt, const QString &url, quint16 lineNumber,
quint16 columnNumber)
{
- QQmlBinding *b = newBinding(property);
+ QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
Q_UNUSED(columnNumber);
b->setNotifyOnValueChanged(true);
@@ -137,7 +139,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString
QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QV4::Value &functionPtr, QObject *obj, QQmlContextData *ctxt)
{
- QQmlBinding *b = newBinding(property);
+ QQmlBinding *b = newBinding(QQmlEnginePrivate::get(ctxt), property);
b->setNotifyOnValueChanged(true);
b->QQmlJavaScriptExpression::setContext(ctxt);
@@ -204,18 +206,13 @@ protected:
const QV4::ScopedFunctionObject &) Q_DECL_OVERRIDE Q_DECL_FINAL
{
QQmlPropertyData pd = getPropertyData();
-
- int idx = pd.coreIndex;
- Q_ASSERT(idx != -1);
-
- int status = -1;
- void *a[] = { &binding, 0, &status, &flags };
- QMetaObject::metacall(*m_target, QMetaObject::WriteProperty, idx, a);
+ pd.writeProperty(*m_target, &binding, flags);
}
};
-template<int StaticPropType>
-class GenericBinding: public QQmlBinding
+// For any target that's not a binding, we have a common doUpdate. However, depending on the type
+// of the target property, there is a specialized write method.
+class QQmlNonbindingBinding: public QQmlBinding
{
protected:
void doUpdate(QQmlBinding *binding, const DeleteWatcher &watcher,
@@ -252,9 +249,16 @@ protected:
ep->dereferenceScarceResources();
}
+ virtual bool write(const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags) = 0;
+};
+
+template<int StaticPropType>
+class GenericBinding: public QQmlNonbindingBinding
+{
+protected:
// Returns true if successful, false if an error description was set on expression
Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined,
- QQmlPropertyData::WriteFlags flags)
+ QQmlPropertyData::WriteFlags flags) Q_DECL_OVERRIDE Q_DECL_FINAL
{
QQmlPropertyData pd = getPropertyData();
int propertyType = StaticPropType; // If the binding is specialized to a type, the if and switch below will be constant-folded.
@@ -572,8 +576,68 @@ Q_ALWAYS_INLINE int QQmlBinding::getPropertyCoreIndex() const
}
}
-QQmlBinding *QQmlBinding::newBinding(const QQmlPropertyData *property)
+class QObjectPointerBinding: public QQmlNonbindingBinding
{
+ QQmlMetaObject targetMetaObject;
+
+public:
+ QObjectPointerBinding(QQmlEnginePrivate *engine, int propertyType)
+ : targetMetaObject(QQmlPropertyPrivate::rawMetaObjectForType(engine, propertyType))
+ {}
+
+protected:
+ Q_ALWAYS_INLINE bool write(const QV4::Value &result, bool isUndefined,
+ QQmlPropertyData::WriteFlags flags) Q_DECL_OVERRIDE Q_DECL_FINAL
+ {
+ QQmlPropertyData pd = getPropertyData();
+ if (Q_UNLIKELY(isUndefined || pd.isValueTypeVirtual()))
+ return slowWrite(pd, result, isUndefined, flags);
+
+ // Check if the result is a QObject:
+ QObject *resultObject = nullptr;
+ QQmlMetaObject resultMo;
+ if (result.isNull()) {
+ // Special case: we can always write a nullptr. Don't bother checking anything else.
+ return pd.writeProperty(targetObject(), &resultObject, flags);
+ } else if (auto wrapper = result.as<QV4::QObjectWrapper>()) {
+ resultObject = wrapper->object();
+ if (!resultObject)
+ return pd.writeProperty(targetObject(), &resultObject, flags);
+ if (QQmlData *ddata = QQmlData::get(resultObject, false))
+ resultMo = ddata->propertyCache;
+ if (resultMo.isNull()) {
+ resultMo = resultObject->metaObject();
+ }
+ } else if (auto variant = result.as<QV4::VariantObject>()) {
+ QVariant value = variant->d()->data;
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context());
+ resultMo = QQmlPropertyPrivate::rawMetaObjectForType(ep, value.userType());
+ if (resultMo.isNull())
+ return slowWrite(pd, result, isUndefined, flags);
+ resultObject = *static_cast<QObject *const *>(value.constData());
+ } else {
+ return slowWrite(pd, result, isUndefined, flags);
+ }
+
+ // Compare & set:
+ if (QQmlMetaObject::canConvert(resultMo, targetMetaObject)) {
+ return pd.writeProperty(targetObject(), &resultObject, flags);
+ } else if (!resultObject && QQmlMetaObject::canConvert(targetMetaObject, resultMo)) {
+ // In the case of a null QObject, we assign the null if there is
+ // any change that the null variant type could be up or down cast to
+ // the property type.
+ return pd.writeProperty(targetObject(), &resultObject, flags);
+ } else {
+ return slowWrite(pd, result, isUndefined, flags);
+ }
+ }
+};
+
+QQmlBinding *QQmlBinding::newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property)
+{
+ if (property && property->isQObject())
+ return new QObjectPointerBinding(engine, property->propType);
+
const int type = (property && property->isFullyResolved()) ? property->propType : QMetaType::UnknownType;
if (type == qMetaTypeId<QQmlBinding *>()) {