From a57bcdde329ef4c9a71aa6ba714f5e30ffd5dc6d Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Tue, 11 Aug 2009 13:59:56 +1000 Subject: Object property binding optimization Add a binding optimization that hits anchors.fill: parent --- src/declarative/qml/qmlbasicscript.cpp | 17 +++++ src/declarative/qml/qmlbasicscript_p.h | 2 + src/declarative/qml/qmlbindingoptimizations.cpp | 92 ++++++++++++++++++++++--- src/declarative/qml/qmlbindingoptimizations_p.h | 35 ++++++++-- src/declarative/qml/qmlcompiler.cpp | 40 ++++++++++- src/declarative/qml/qmlcompiler_p.h | 1 + src/declarative/qml/qmlcontext_p.h | 4 +- src/declarative/qml/qmlinstruction.cpp | 2 +- src/declarative/qml/qmlinstruction_p.h | 11 ++- src/declarative/qml/qmlvme.cpp | 22 +++++- 10 files changed, 202 insertions(+), 24 deletions(-) diff --git a/src/declarative/qml/qmlbasicscript.cpp b/src/declarative/qml/qmlbasicscript.cpp index 8993845..f3f9289 100644 --- a/src/declarative/qml/qmlbasicscript.cpp +++ b/src/declarative/qml/qmlbasicscript.cpp @@ -714,6 +714,23 @@ int QmlBasicScript::singleIdFetchIndex() const return d->instructions()[0].fetch.idx; } +bool QmlBasicScript::isSingleContextProperty() const +{ + if (!isValid()) + return false; + + return d->instructionCount == 1 && + d->instructions()[0].type == ScriptInstruction::FetchContextConstant; +} + +int QmlBasicScript::singleContextPropertyIndex() const +{ + if (!isSingleContextProperty()) + return -1; + + return d->instructions()[0].constant.idx; +} + /*! Return a pointer to the script's compile data, or null if there is no data. */ diff --git a/src/declarative/qml/qmlbasicscript_p.h b/src/declarative/qml/qmlbasicscript_p.h index d096746..5ad7735 100644 --- a/src/declarative/qml/qmlbasicscript_p.h +++ b/src/declarative/qml/qmlbasicscript_p.h @@ -101,6 +101,8 @@ public: bool isSingleIdFetch() const; int singleIdFetchIndex() const; + bool isSingleContextProperty() const; + int singleContextPropertyIndex() const; private: int flags; QmlBasicScriptPrivate *d; diff --git a/src/declarative/qml/qmlbindingoptimizations.cpp b/src/declarative/qml/qmlbindingoptimizations.cpp index 235c034..a10b1e3 100644 --- a/src/declarative/qml/qmlbindingoptimizations.cpp +++ b/src/declarative/qml/qmlbindingoptimizations.cpp @@ -44,17 +44,27 @@ QT_BEGIN_NAMESPACE +/* + The QmlBinding_Id optimization handles expressions of the type: -QmlBindingIdOptimization::QmlBindingIdOptimization(QObject *object, - int propertyIdx, - QmlContext *context, - int id) + property: id + + where id is a local context id, and property is an object property. + Coercian between id and property must be checked outside the QmlBinding_Id - + it assumes that they coerce successfully. + + The QmlBinding_Id class avoids any signal slot connections, through the + special "bindings" linked list maintained in the + QmlContextPrivate::ContextGuard instance for each id object. +*/ +QmlBinding_Id::QmlBinding_Id(QObject *object, int propertyIdx, + QmlContext *context, int id) : m_prev(0), m_next(0), m_object(object), m_propertyIdx(propertyIdx), m_id(id) { QmlAbstractExpression::setContext(context); } -void QmlBindingIdOptimization::setEnabled(bool e) +void QmlBinding_Id::setEnabled(bool e) { if (e) { addToObject(m_object); @@ -64,12 +74,12 @@ void QmlBindingIdOptimization::setEnabled(bool e) } } -int QmlBindingIdOptimization::propertyIndex() +int QmlBinding_Id::propertyIndex() { return m_propertyIdx; } -void QmlBindingIdOptimization::update() +void QmlBinding_Id::update() { QmlContextPrivate *ctxtPriv = static_cast(QObjectPrivate::get(context())); @@ -91,7 +101,7 @@ void QmlBindingIdOptimization::update() } } -void QmlBindingIdOptimization::reset() +void QmlBinding_Id::reset() { if (m_prev) { *m_prev = m_next; @@ -106,4 +116,70 @@ void QmlBindingIdOptimization::reset() m_propertyIdx, a); } +/* + The QmlBinding_ObjectProperty optimization handles expressions of the type: + + property: objectProperty + + where both property and objectProperty are object properties on the target + object. Coercian between the two must be checked outside the + QmlBinding_ObjectProperty - it assumes that they coerce successfully. + + Due to dot properties, property does not have to be on the same object as + objectProperty. For example: + + anchors.fill: parent +*/ +QmlBinding_ObjProperty::QmlBinding_ObjProperty(QObject *object, int propertyIdx, + QObject *context, int contextIdx, + int notifyIdx) +: m_enabled(false), m_object(object), m_propertyIdx(propertyIdx), + m_context(context), m_contextIdx(contextIdx), m_notifyIdx(notifyIdx) +{ +} + +void QmlBinding_ObjProperty::setEnabled(bool e) +{ + m_enabled = e; + if (e) { + addToObject(m_object); + update(); + } else { + removeFromObject(); + } +} + +int QmlBinding_ObjProperty::propertyIndex() +{ + return m_propertyIdx; +} + +void QmlBinding_ObjProperty::update() +{ + if (!m_enabled) + return; + + QObject *value = 0; + void *a[] = { &value, 0 }; + + // Read + QMetaObject::metacall(m_context, QMetaObject::ReadProperty, + m_contextIdx, a); + + // Write + QMetaObject::metacall(m_object, QMetaObject::WriteProperty, + m_propertyIdx, a); + + // Connect notify if needed. Only need to connect once, so we set + // m_notifyIdx back to -1 afterwards + static int slotIdx = -1; + if (m_notifyIdx != -1) { + if (slotIdx == -1) + slotIdx = QmlBinding_ObjProperty::staticMetaObject.indexOfMethod("update()"); + + QMetaObject::connect(m_context, m_notifyIdx, this, slotIdx); + m_notifyIdx = -1; + } +} + QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlbindingoptimizations_p.h b/src/declarative/qml/qmlbindingoptimizations_p.h index f50972d..2d2ffec 100644 --- a/src/declarative/qml/qmlbindingoptimizations_p.h +++ b/src/declarative/qml/qmlbindingoptimizations_p.h @@ -60,11 +60,11 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -class QmlBindingIdOptimization : public QmlAbstractExpression, - public QmlAbstractBinding +class QmlBinding_Id : public QmlAbstractExpression, + public QmlAbstractBinding { public: - QmlBindingIdOptimization(QObject *object, int propertyIdx, + QmlBinding_Id(QObject *object, int propertyIdx, QmlContext *context, int id); // Inherited from QmlAbstractBinding @@ -75,14 +75,39 @@ public: void reset(); private: - QmlBindingIdOptimization **m_prev; - QmlBindingIdOptimization *m_next; + QmlBinding_Id **m_prev; + QmlBinding_Id *m_next; QObject *m_object; int m_propertyIdx; int m_id; }; +class QmlBinding_ObjProperty : public QObject, + public QmlAbstractExpression, + public QmlAbstractBinding +{ + Q_OBJECT +public: + QmlBinding_ObjProperty(QObject *object, int propertyIdx, + QObject *context, int contextIdx, int notifyIdx); + + // Inherited from QmlAbstractBinding + virtual void setEnabled(bool); + virtual int propertyIndex(); + +private slots: + virtual void update(); + +private: + bool m_enabled; + QObject *m_object; + int m_propertyIdx; + QObject *m_context; + int m_contextIdx; + int m_notifyIdx; +}; + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index 7c0964b..75ed94b 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -2069,6 +2069,7 @@ void QmlCompiler::genBindingAssignment(QmlParser::Value *binding, QmlBasicScript bs; if (ref.isBasicScript) bs.load(ref.compiledData.constData() + sizeof(quint32)); + if (bs.isSingleIdFetch()) { int idIndex = bs.singleIdFetchIndex(); QmlParser::Object *idObj = compileState.idIndexes.value(idIndex); @@ -2079,9 +2080,26 @@ void QmlCompiler::genBindingAssignment(QmlParser::Value *binding, output->bytecode << store; return; } + } else if (bs.isSingleContextProperty()) { + int propIndex = bs.singleContextPropertyIndex(); + + QMetaProperty p = + ref.bindingContext.object->metaObject()->property(propIndex); + if ((p.notifySignalIndex() != -1 || p.isConstant()) && + canCoerce(prop->type, p.userType())) { + + store.type = QmlInstruction::StoreObjPropBinding; + store.assignObjPropBinding.property = prop->index; + store.assignObjPropBinding.contextIdx = propIndex; + store.assignObjPropBinding.context = ref.bindingContext.stack; + store.assignObjPropBinding.notifyIdx = p.notifySignalIndex(); + + output->bytecode << store; + return; + } } - store.type = QmlInstruction::StoreCompiledBinding; + store.type = QmlInstruction::StoreBinding; store.assignBinding.value = output->indexForByteArray(ref.compiledData); store.assignBinding.context = ref.bindingContext.stack; store.assignBinding.owner = ref.bindingContext.owner; @@ -2157,8 +2175,7 @@ bool QmlCompiler::completeComponentBuild() */ bool QmlCompiler::canCoerce(int to, QmlParser::Object *from) { - const QMetaObject *toMo = - QmlMetaType::rawMetaObjectForType(to); + const QMetaObject *toMo = QmlMetaType::rawMetaObjectForType(to); const QMetaObject *fromMo = from->metaObject(); while (fromMo) { @@ -2169,6 +2186,23 @@ bool QmlCompiler::canCoerce(int to, QmlParser::Object *from) return false; } +/*! + Returns true if from can be assigned to a (QObject) property of type + to. +*/ +bool QmlCompiler::canCoerce(int to, int from) +{ + const QMetaObject *toMo = QmlMetaType::rawMetaObjectForType(to); + const QMetaObject *fromMo = QmlMetaType::rawMetaObjectForType(from); + + while (fromMo) { + if (fromMo == toMo) + return true; + fromMo = fromMo->superClass(); + } + return false; +} + QmlType *QmlCompiler::toQmlType(QmlParser::Object *from) { // ### Optimize diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h index 3d79e32..4f56169 100644 --- a/src/declarative/qml/qmlcompiler_p.h +++ b/src/declarative/qml/qmlcompiler_p.h @@ -226,6 +226,7 @@ private: static int findSignalByName(const QMetaObject *, const QByteArray &name); static bool canCoerce(int to, QmlParser::Object *from); + static bool canCoerce(int to, int from); static QmlType *toQmlType(QmlParser::Object *from); QStringList deferredProperties(QmlParser::Object *); diff --git a/src/declarative/qml/qmlcontext_p.h b/src/declarative/qml/qmlcontext_p.h index b5479d9..84d990c 100644 --- a/src/declarative/qml/qmlcontext_p.h +++ b/src/declarative/qml/qmlcontext_p.h @@ -69,7 +69,7 @@ class QmlEngine; class QmlExpression; class QmlExpressionPrivate; class QmlAbstractExpression; -class QmlBindingIdOptimization; +class QmlBinding_Id; class QmlContextPrivate : public QObjectPrivate { @@ -114,7 +114,7 @@ public: { ContextGuard() : priv(0), bindings(0) {} QmlContextPrivate *priv; - QmlBindingIdOptimization *bindings; + QmlBinding_Id *bindings; ContextGuard &operator=(QObject *obj) { (QGuard&)*this = obj; return *this; } diff --git a/src/declarative/qml/qmlinstruction.cpp b/src/declarative/qml/qmlinstruction.cpp index 889a057..fd912ac 100644 --- a/src/declarative/qml/qmlinstruction.cpp +++ b/src/declarative/qml/qmlinstruction.cpp @@ -140,7 +140,7 @@ void QmlCompiledData::dump(QmlInstruction *instr, int idx) case QmlInstruction::AssignCustomType: qWarning() << idx << "\t" << line << "\t" << "ASSIGN_CUSTOMTYPE\t\t" << instr->assignCustomType.propertyIndex << "\t" << instr->assignCustomType.valueIndex; break; - case QmlInstruction::StoreCompiledBinding: + case QmlInstruction::StoreBinding: qWarning() << idx << "\t" << line << "\t" << "STORE_COMPILED_BINDING\t" << instr->assignBinding.property << "\t" << instr->assignBinding.value << "\t\t" << instr->assignBinding.context; break; case QmlInstruction::StoreValueSource: diff --git a/src/declarative/qml/qmlinstruction_p.h b/src/declarative/qml/qmlinstruction_p.h index 16bc8cf..7f3498f 100644 --- a/src/declarative/qml/qmlinstruction_p.h +++ b/src/declarative/qml/qmlinstruction_p.h @@ -122,10 +122,11 @@ public: // Unresolved single assignment // AssignSignalObject, /* assignSignalObject */ - AssignCustomType, /* assignCustomType */ + AssignCustomType, /* assignCustomType */ - StoreCompiledBinding, /* assignBinding */ + StoreBinding, /* assignBinding */ StoreIdOptBinding, /* assignIdOptBinding */ + StoreObjPropBinding, /* assignObjPropBinding */ StoreValueSource, /* assignValueSource */ BeginObject, /* begin */ @@ -194,6 +195,12 @@ public: } assignIdOptBinding; struct { int property; + int contextIdx; + short context; + short notifyIdx; + } assignObjPropBinding; + struct { + int property; } fetch; struct { int property; diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index dccf5c4..fdfeddc 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -529,7 +529,7 @@ QObject *QmlVME::run(QStack &stack, QmlContext *ctxt, QmlCompiledData } break; - case QmlInstruction::StoreCompiledBinding: + case QmlInstruction::StoreBinding: { QObject *target = stack.at(stack.count() - 1 - instr.assignBinding.owner); @@ -554,14 +554,30 @@ QObject *QmlVME::run(QStack &stack, QmlContext *ctxt, QmlCompiledData { QObject *target = stack.top(); - QmlBindingIdOptimization *bind = - new QmlBindingIdOptimization(target, instr.assignIdOptBinding.property, ctxt, instr.assignIdOptBinding.id); + QmlBinding_Id *bind = + new QmlBinding_Id(target, instr.assignIdOptBinding.property, + ctxt, instr.assignIdOptBinding.id); bindValues.append(bind); // ### Need a mePtr bind->addToObject(target); } break; + case QmlInstruction::StoreObjPropBinding: + { + QObject *target = stack.top(); + QObject *context = + stack.at(stack.count() - 1 - instr.assignObjPropBinding.context); + + QmlBinding_ObjProperty *bind = + new QmlBinding_ObjProperty(target, instr.assignObjPropBinding.property, context, instr.assignObjPropBinding.contextIdx, instr.assignObjPropBinding.notifyIdx); + + bindValues.append(bind); + // ### Need a mePtr + bind->addToObject(target); + } + break; + case QmlInstruction::StoreValueSource: { QmlPropertyValueSource *vs = -- cgit v0.12