diff options
-rw-r--r-- | src/declarative/qml/qmlbinding.cpp | 5 | ||||
-rw-r--r-- | src/declarative/qml/qmlbinding.h | 2 | ||||
-rw-r--r-- | src/declarative/qml/qmlbindingoptimizations.cpp | 112 | ||||
-rw-r--r-- | src/declarative/qml/qmlbindingoptimizations_p.h | 54 | ||||
-rw-r--r-- | src/declarative/qml/qmlbindingvme.cpp | 324 | ||||
-rw-r--r-- | src/declarative/qml/qmlbindingvme_p.h | 25 | ||||
-rw-r--r-- | src/declarative/qml/qmlcompiler.cpp | 29 | ||||
-rw-r--r-- | src/declarative/qml/qmlcompiler_p.h | 4 | ||||
-rw-r--r-- | src/declarative/qml/qmlengine.cpp | 2 | ||||
-rw-r--r-- | src/declarative/qml/qmlinstruction_p.h | 1 | ||||
-rw-r--r-- | src/declarative/qml/qmlvme.cpp | 22 |
11 files changed, 408 insertions, 172 deletions
diff --git a/src/declarative/qml/qmlbinding.cpp b/src/declarative/qml/qmlbinding.cpp index 56d5807..d1a8fe8 100644 --- a/src/declarative/qml/qmlbinding.cpp +++ b/src/declarative/qml/qmlbinding.cpp @@ -276,6 +276,11 @@ QmlAbstractBinding::~QmlAbstractBinding() *m_mePtr = 0; } +void QmlAbstractBinding::destroy() +{ + delete this; +} + void QmlAbstractBinding::addToObject(QObject *object) { Q_ASSERT(object); diff --git a/src/declarative/qml/qmlbinding.h b/src/declarative/qml/qmlbinding.h index 6f2d4f1..177a0fd 100644 --- a/src/declarative/qml/qmlbinding.h +++ b/src/declarative/qml/qmlbinding.h @@ -61,6 +61,8 @@ public: QmlAbstractBinding(); virtual ~QmlAbstractBinding(); + virtual void destroy(); + virtual QString expression() const; void setEnabled(bool e) { setEnabled(e, QmlMetaProperty::DontRemoveBinding); } diff --git a/src/declarative/qml/qmlbindingoptimizations.cpp b/src/declarative/qml/qmlbindingoptimizations.cpp index 120e07b..d8a2de0 100644 --- a/src/declarative/qml/qmlbindingoptimizations.cpp +++ b/src/declarative/qml/qmlbindingoptimizations.cpp @@ -47,32 +47,50 @@ QT_BEGIN_NAMESPACE -int QmlBinding_Basic::reevalIndex = -1; +int QmlOptimizedBindings::methodCount = -1; -QmlBinding_Basic::QmlBinding_Basic(QObject *target, int property, - const char *data, QmlRefCount *ref, - QObject *scope, QmlContext *context) -: m_enabled(false), m_updating(false), m_scope(scope), m_target(target), - m_property(property), m_data(data) +QmlOptimizedBindings::QmlOptimizedBindings(const char *program, QmlContext *context) +: m_program(program) { - if (reevalIndex == -1) - reevalIndex = QmlBinding_Basic::staticMetaObject.indexOfSlot("reeval()"); + if (methodCount == -1) + methodCount = QmlOptimizedBindings::staticMetaObject.methodCount(); m_config.target = this; - m_config.targetSlot = reevalIndex; - m_scope2 = m_scope; + m_config.targetSlot = metaObject()->methodCount(); + + quint32 bindings = 0; + QmlBindingVME::init(m_program, &m_config, &m_signalTable, &bindings); + + m_bindings = new Binding[bindings]; QmlAbstractExpression::setContext(context); } -QmlBinding_Basic::~QmlBinding_Basic() +QmlOptimizedBindings::~QmlOptimizedBindings() { + delete [] m_bindings; } -void QmlBinding_Basic::setEnabled(bool e, QmlMetaProperty::WriteFlags flags) +QmlAbstractBinding *QmlOptimizedBindings::configBinding(int index, QObject *target, + QObject *scope, int property) +{ + Binding *rv = m_bindings + index; + + rv->index = index; + rv->property = property; + rv->target = target; + rv->scope = scope; + rv->parent = this; + + addref(); // This is decremented in Binding::destroy() + + return rv; +} + +void QmlOptimizedBindings::Binding::setEnabled(bool e, QmlMetaProperty::WriteFlags flags) { if (e) { - addToObject(m_target); + addToObject(target); update(flags); } else { removeFromObject(); @@ -80,56 +98,74 @@ void QmlBinding_Basic::setEnabled(bool e, QmlMetaProperty::WriteFlags flags) QmlAbstractBinding::setEnabled(e, flags); - if (m_enabled != e) { - m_enabled = e; + if (enabled != e) { + enabled = e; if (e) update(flags); } } -int QmlBinding_Basic::propertyIndex() +int QmlOptimizedBindings::Binding::propertyIndex() { - return m_property & 0xFFFF; + return property & 0xFFFF; } -void QmlBinding_Basic::update(QmlMetaProperty::WriteFlags flags) +void QmlOptimizedBindings::Binding::update(QmlMetaProperty::WriteFlags) { - if (!m_enabled) - return; + parent->run(this); +} - if (m_updating) { - qmlInfo(m_target) << tr("Binding loop detected"); - return; +void QmlOptimizedBindings::Binding::destroy() +{ + enabled = false; + removeFromObject(); + parent->release(); +} + +int QmlOptimizedBindings::qt_metacall(QMetaObject::Call c, int id, void **) +{ + if (c == QMetaObject::InvokeMetaMethod && id >= methodCount) { + id -= methodCount; + + quint32 *reeval = m_signalTable + m_signalTable[id]; + quint32 count = *reeval; + ++reeval; + for (quint32 ii = 0; ii < count; ++ii) { + run(m_bindings + reeval[ii]); + } } + return -1; +} + +void QmlOptimizedBindings::run(Binding *binding) +{ + if (!binding->enabled) + return; + if (binding->updating) + qWarning("ERROR: Circular binding"); QmlContext *context = QmlAbstractExpression::context(); if (!context) return; QmlContextPrivate *cp = QmlContextPrivate::get(context); - m_updating = true; - - if (m_property & 0xFFFF0000) { + if (binding->property & 0xFFFF0000) { QmlEnginePrivate *ep = QmlEnginePrivate::get(cp->engine); - QmlValueType *vt = ep->valueTypes[(m_property >> 16) & 0xFF]; + QmlValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF]; Q_ASSERT(vt); - vt->read(m_target, m_property & 0xFFFF); + vt->read(binding->target, binding->property & 0xFFFF); QObject *target = vt; - QmlBindingVME::run(m_data, &m_config, cp, &m_scope, &target); + QmlBindingVME::run(m_program, binding->index, &m_config, cp, + &binding->scope, &target); - vt->write(m_target, m_property & 0xFFFF, flags); + vt->write(binding->target, binding->property & 0xFFFF, + QmlMetaProperty::DontRemoveBinding); } else { - QmlBindingVME::run(m_data, &m_config, cp, &m_scope, &m_target); + QmlBindingVME::run(m_program, binding->index, &m_config, cp, + &binding->scope, &binding->target); } - - m_updating = false; -} - -void QmlBinding_Basic::reeval() -{ - update(QmlMetaProperty::DontRemoveBinding); } /* diff --git a/src/declarative/qml/qmlbindingoptimizations_p.h b/src/declarative/qml/qmlbindingoptimizations_p.h index ee2eb83..6289cc0 100644 --- a/src/declarative/qml/qmlbindingoptimizations_p.h +++ b/src/declarative/qml/qmlbindingoptimizations_p.h @@ -62,36 +62,44 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -class QmlBinding_Basic : public QObject, - public QmlAbstractExpression, - public QmlAbstractBinding +class QmlOptimizedBindings : public QObject, public QmlAbstractExpression, public QmlRefCount { - Q_OBJECT public: - QmlBinding_Basic(QObject *target, int property, - const char *data, QmlRefCount *ref, - QObject *scope, QmlContext *context); - virtual ~QmlBinding_Basic(); + QmlOptimizedBindings(const char *program, QmlContext *context); + virtual ~QmlOptimizedBindings(); + QmlAbstractBinding *configBinding(int index, QObject *target, QObject *scope, int property); - // Inherited from QmlAbstractBinding - virtual void setEnabled(bool, QmlMetaProperty::WriteFlags flags); - virtual int propertyIndex(); - virtual void update(QmlMetaProperty::WriteFlags flags); - -private slots: - void reeval(); +protected: + int qt_metacall(QMetaObject::Call, int, void **); private: - bool m_enabled:1; - bool m_updating:1; - QObject *m_scope; - QObject *m_target; - int m_property; - const char *m_data; + struct Binding : public QmlAbstractBinding { + Binding() : enabled(false), updating(0), property(0), + scope(0), target(0), parent(0) {} + + // Inherited from QmlAbstractBinding + virtual void setEnabled(bool, QmlMetaProperty::WriteFlags flags); + virtual int propertyIndex(); + virtual void update(QmlMetaProperty::WriteFlags flags); + virtual void destroy(); + + int index:30; + bool enabled:1; + bool updating:1; + int property; + QObject *scope; + QObject *target; + + QmlOptimizedBindings *parent; + }; + void run(Binding *); + QmlBindingVME::Config m_config; - QGuard<QObject> m_scope2; + const char *m_program; + Binding *m_bindings; + quint32 *m_signalTable; - static int reevalIndex; + static int methodCount; }; class QmlBinding_Id : public QmlAbstractExpression, diff --git a/src/declarative/qml/qmlbindingvme.cpp b/src/declarative/qml/qmlbindingvme.cpp index e5cbdfc..d85d7ba 100644 --- a/src/declarative/qml/qmlbindingvme.cpp +++ b/src/declarative/qml/qmlbindingvme.cpp @@ -79,8 +79,6 @@ struct Instr { enum { Noop, - Init, // init - Subscribe, // subscribe SubscribeId, // subscribe @@ -224,13 +222,18 @@ struct Instr { }; struct Program { - int dataLength; + quint32 bindings; + quint32 dataLength; + quint32 signalTableOffset; + quint16 subscriptions; + quint16 identifiers; + const char *data() const { return ((const char *)this) + sizeof(Program); } const Instr *instructions() const { return (const Instr *)(data() + dataLength); } }; } -struct QmlBindingCompiler +struct QmlBindingCompilerPrivate { struct Result { Result() : unknownType(false), metaObject(0), type(-1), reg(-1) {} @@ -251,8 +254,12 @@ struct QmlBindingCompiler QSet<QString> subscriptionSet; }; - QmlBindingCompiler() : registers(0), strings(0) {} - void reset(); + QmlBindingCompilerPrivate() : registers(0), strings(0) { + committed.strings = 0; + } + + void resetInstanceState(); + int commitCompile(); QmlParser::Object *context; QmlParser::Object *component; @@ -261,6 +268,8 @@ struct QmlBindingCompiler QmlEnginePrivate::Imports imports; QmlEnginePrivate *engine; + QString contextName() const { return QLatin1String("$$$SCOPE_") + QString::number((intptr_t)context, 16); } + bool compile(QmlJS::AST::Node *); bool parseExpression(QmlJS::AST::Node *, Result &); @@ -293,60 +302,44 @@ struct QmlBindingCompiler int strings; QByteArray data; - QSet<QString> subscriptionSet; - QHash<QString, int> subscriptionIds; bool subscription(const QStringList &, Result *); int subscriptionIndex(const QStringList &); bool subscriptionNeutral(const QSet<QString> &base, const QSet<QString> &lhs, const QSet<QString> &rhs); - QVector<Instr> bytecode; -}; -QByteArray QmlBindingVME::compile(const QmlBasicScript::Expression &expression, QmlEnginePrivate *engine) -{ - if (!expression.expression.asAST()) return false; + QSet<int> usedSubscriptionIds; + QSet<QString> subscriptionSet; + QHash<QString, int> subscriptionIds; + QVector<Instr> bytecode; - QmlBindingCompiler bsc; - bsc.context = expression.context; - bsc.component = expression.component; - bsc.destination = expression.property; - bsc.ids = expression.ids; - bsc.imports = expression.imports; - bsc.engine = engine; + // Committed binding data + struct { + QList<int> offsets; + QList<QSet<int> > dependencies; - bool ok = bsc.compile(expression.expression.asAST()); + QVector<Instr> bytecode; + QByteArray data; + int strings; + QHash<QString, int> subscriptionIds; - if (ok) { - Program prog; - prog.dataLength = 4 * ((bsc.data.size() + 3) / 4); - int size = sizeof(Program) + bsc.bytecode.count() * sizeof(Instr); - size += prog.dataLength; + int count() const { return offsets.count(); } + } committed; - QByteArray data; - data.resize(size); - memcpy(data.data(), &prog, sizeof(Program)); - if (prog.dataLength) - memcpy((char *)((Program *)data.data())->data(), bsc.data.constData(), - bsc.data.size()); - memcpy((char *)((Program *)data.data())->instructions(), bsc.bytecode.constData(), - bsc.bytecode.count() * sizeof(Instr)); - return data; - } else { - return QByteArray(); - } -} + QByteArray buildSignalTable() const; +}; -inline void subscribe(QObject *o, int notifyIndex, QmlBindingVME::Config::Subscription *s, - QmlBindingVME::Config *config) +inline void subscribe(QObject *o, int notifyIndex, + int subIndex, QmlBindingVME::Config *config) { + QmlBindingVME::Config::Subscription *s = config->subscriptions + subIndex; if (o != s->source || notifyIndex != s->notifyIndex) { if (s->source) QMetaObject::disconnect(s->source, s->notifyIndex, - config->target, config->targetSlot); + config->target, config->targetSlot + subIndex); s->source = o; s->notifyIndex = notifyIndex; - if (s->source && s->notifyIndex != -1) + if (s->source && s->notifyIndex != -1) QMetaObject::connect(s->source, s->notifyIndex, config->target, - config->targetSlot, Qt::DirectConnection); + config->targetSlot + subIndex, Qt::DirectConnection); } } @@ -436,7 +429,7 @@ static bool findproperty(QObject *obj, } if (subIdx != -1) - subscribe(obj, property->notifyIndex, config->subscriptions + subIdx, config); + subscribe(obj, property->notifyIndex, subIdx, config); return true; } else { @@ -588,8 +581,24 @@ inline static QUrl toUrl(Register *reg, int type, QmlContextPrivate *context, bo return base; } -void QmlBindingVME::run(const char *programData, Config *config, - QmlContextPrivate *context, +/*! +Returns the signal/binding table. +*/ +void QmlBindingVME::init(const char *programData, Config *config, + quint32 **sigTable, quint32 *bindingCount) +{ + Program *program = (Program *)programData; + if (program->subscriptions) + config->subscriptions = new Config::Subscription[program->subscriptions]; + if (program->identifiers) + config->identifiers = new QScriptDeclarativeClass::PersistentIdentifier[program->identifiers]; + + *sigTable = (quint32 *)(program->data() + program->signalTableOffset); + *bindingCount = program->bindings; +} + +void QmlBindingVME::run(const char *programData, int instrIndex, + Config *config, QmlContextPrivate *context, QObject **scopes, QObject **outputs) { Register registers[32]; @@ -598,6 +607,7 @@ void QmlBindingVME::run(const char *programData, Config *config, QmlEnginePrivate *engine = QmlEnginePrivate::get(context->engine); Program *program = (Program *)programData; const Instr *instr = program->instructions(); + instr += instrIndex; const char *data = program->data(); while (instr) { @@ -605,18 +615,11 @@ void QmlBindingVME::run(const char *programData, Config *config, switch (instr->type) { case Instr::Noop: break; - case Instr::Init: - if (!config->subscriptions && instr->init.subscriptions) - config->subscriptions = new Config::Subscription[instr->init.subscriptions]; - if (!config->identifiers && instr->init.identifiers) - config->identifiers = new QScriptDeclarativeClass::PersistentIdentifier[instr->init.identifiers]; - break; case Instr::SubscribeId: case Instr::Subscribe: { QObject *o = registers[instr->subscribe.reg].getQObject(); - Config::Subscription *s = config->subscriptions + instr->subscribe.offset; int notifyIndex = instr->subscribe.index; if (instr->type == Instr::SubscribeId) { @@ -624,7 +627,7 @@ void QmlBindingVME::run(const char *programData, Config *config, notifyIndex += context->notifyIndex; } - subscribe(o, instr->subscribe.index, s, config); + subscribe(o, instr->subscribe.index, instr->subscribe.offset, config); } break; @@ -852,6 +855,11 @@ void QmlBindingVME::dump(const QByteArray &programData) { const Program *program = (const Program *)programData.constData(); + qWarning() << "Program.bindings:" << program->bindings; + qWarning() << "Program.dataLength:" << program->dataLength; + qWarning() << "Program.subscriptions:" << program->subscriptions; + qWarning() << "Program.indentifiers:" << program->identifiers; + int count = (programData.size() - sizeof(Program) - program->dataLength) / sizeof(Instr); const Instr *instr = program->instructions(); @@ -861,9 +869,6 @@ void QmlBindingVME::dump(const QByteArray &programData) case Instr::Noop: qWarning().nospace() << "Noop"; break; - case Instr::Init: - qWarning().nospace() << "Init" << "\t\t\t" << instr->init.subscriptions << "\t" << instr->init.identifiers; - break; case Instr::Subscribe: qWarning().nospace() << "Subscribe" << "\t\t" << instr->subscribe.offset << "\t" << instr->subscribe.reg << "\t" << instr->subscribe.index; break; @@ -990,18 +995,42 @@ void QmlBindingVME::dump(const QByteArray &programData) } } -void QmlBindingCompiler::reset() +/*! +Clear the state associated with attempting to compile a specific binding. +This does not clear the global "commited binding" states. +*/ +void QmlBindingCompilerPrivate::resetInstanceState() { registers = 0; - strings = 0; - data.clear(); + strings = committed.strings; + data = committed.data; + subscriptionIds = committed.subscriptionIds; subscriptionSet.clear(); + usedSubscriptionIds.clear(); bytecode.clear(); } -bool QmlBindingCompiler::compile(QmlJS::AST::Node *node) +/*! +Mark the last compile as successful, and add it to the "committed data" +section. + +Returns the index for the committed binding. +*/ +int QmlBindingCompilerPrivate::commitCompile() { - reset(); + int rv = committed.count(); + committed.offsets << committed.bytecode.count(); + committed.dependencies << usedSubscriptionIds; + committed.bytecode << bytecode; + committed.data = data; + committed.strings = strings; + committed.subscriptionIds = subscriptionIds; + return rv; +} + +bool QmlBindingCompilerPrivate::compile(QmlJS::AST::Node *node) +{ + resetInstanceState(); Result type; @@ -1012,12 +1041,6 @@ bool QmlBindingCompiler::compile(QmlJS::AST::Node *node) if (subscriptionSet.count() > 0xFFFF || strings > 0xFFFF) return false; - - Instr init; - init.type = Instr::Init; - init.init.subscriptions = subscriptionIds.count(); - init.init.identifiers = strings; - bytecode.prepend(init); } if (type.unknownType) { @@ -1141,7 +1164,7 @@ bool QmlBindingCompiler::compile(QmlJS::AST::Node *node) } } -bool QmlBindingCompiler::parseExpression(QmlJS::AST::Node *node, Result &type) +bool QmlBindingCompilerPrivate::parseExpression(QmlJS::AST::Node *node, Result &type) { while (node->kind == AST::Node::Kind_NestedExpression) node = static_cast<AST::NestedExpression *>(node)->expression; @@ -1162,13 +1185,13 @@ bool QmlBindingCompiler::parseExpression(QmlJS::AST::Node *node, Result &type) return true; } -bool QmlBindingCompiler::tryName(QmlJS::AST::Node *node) +bool QmlBindingCompilerPrivate::tryName(QmlJS::AST::Node *node) { return node->kind == AST::Node::Kind_IdentifierExpression || node->kind == AST::Node::Kind_FieldMemberExpression; } -bool QmlBindingCompiler::parseName(AST::Node *node, Result &type) +bool QmlBindingCompilerPrivate::parseName(AST::Node *node, Result &type) { QStringList nameParts; if (!buildName(nameParts, node)) @@ -1226,11 +1249,12 @@ bool QmlBindingCompiler::parseName(AST::Node *node, Result &type) attach.attached.index = attachType->index(); bytecode << attach; + subscribeName << contextName(); + subscribeName << QLatin1String("$$$ATTACH_") + name; + absType = 0; type.metaObject = attachType->attachedPropertiesType(); - subscribeName << QLatin1String("$$$ATTACH_") + name; - continue; } else if (ids.contains(name)) { QmlParser::Object *idObject = ids.value(name); @@ -1287,7 +1311,7 @@ bool QmlBindingCompiler::parseName(AST::Node *node, Result &type) instr.load.reg = reg; bytecode << instr; - subscribeName << QLatin1String("$$$Scope"); + subscribeName << contextName(); subscribeName << name; fetch(type, context->metaObject(), reg, d0Idx, subscribeName); @@ -1298,7 +1322,7 @@ bool QmlBindingCompiler::parseName(AST::Node *node, Result &type) instr.load.reg = reg; bytecode << instr; - subscribeName << QLatin1String("$$$Root"); + subscribeName << QLatin1String("$$$ROOT"); subscribeName << name; fetch(type, component->metaObject(), reg, d1Idx, subscribeName); @@ -1395,7 +1419,7 @@ bool QmlBindingCompiler::parseName(AST::Node *node, Result &type) return true; } -bool QmlBindingCompiler::tryArith(QmlJS::AST::Node *node) +bool QmlBindingCompilerPrivate::tryArith(QmlJS::AST::Node *node) { if (node->kind != AST::Node::Kind_BinaryExpression) return false; @@ -1408,7 +1432,7 @@ bool QmlBindingCompiler::tryArith(QmlJS::AST::Node *node) return false; } -bool QmlBindingCompiler::parseArith(QmlJS::AST::Node *node, Result &type) +bool QmlBindingCompilerPrivate::parseArith(QmlJS::AST::Node *node, Result &type) { AST::BinaryExpression *expression = static_cast<AST::BinaryExpression *>(node); @@ -1473,7 +1497,7 @@ bool QmlBindingCompiler::parseArith(QmlJS::AST::Node *node, Result &type) return true; } -bool QmlBindingCompiler::tryLogic(QmlJS::AST::Node *node) +bool QmlBindingCompilerPrivate::tryLogic(QmlJS::AST::Node *node) { if (node->kind != AST::Node::Kind_BinaryExpression) return false; @@ -1487,7 +1511,7 @@ bool QmlBindingCompiler::tryLogic(QmlJS::AST::Node *node) return false; } -bool QmlBindingCompiler::parseLogic(QmlJS::AST::Node *node, Result &type) +bool QmlBindingCompilerPrivate::parseLogic(QmlJS::AST::Node *node, Result &type) { AST::BinaryExpression *expression = static_cast<AST::BinaryExpression *>(node); @@ -1542,12 +1566,12 @@ bool QmlBindingCompiler::parseLogic(QmlJS::AST::Node *node, Result &type) return true; } -bool QmlBindingCompiler::tryConditional(QmlJS::AST::Node *node) +bool QmlBindingCompilerPrivate::tryConditional(QmlJS::AST::Node *node) { return (node->kind == AST::Node::Kind_ConditionalExpression); } -bool QmlBindingCompiler::parseConditional(QmlJS::AST::Node *node, Result &type) +bool QmlBindingCompilerPrivate::parseConditional(QmlJS::AST::Node *node, Result &type) { AST::ConditionalExpression *expression = static_cast<AST::ConditionalExpression *>(node); @@ -1610,14 +1634,14 @@ bool QmlBindingCompiler::parseConditional(QmlJS::AST::Node *node, Result &type) return true; } -bool QmlBindingCompiler::tryConstant(QmlJS::AST::Node *node) +bool QmlBindingCompilerPrivate::tryConstant(QmlJS::AST::Node *node) { return node->kind == AST::Node::Kind_TrueLiteral || node->kind == AST::Node::Kind_FalseLiteral || node->kind == AST::Node::Kind_NumericLiteral; } -bool QmlBindingCompiler::parseConstant(QmlJS::AST::Node *node, Result &type) +bool QmlBindingCompilerPrivate::parseConstant(QmlJS::AST::Node *node, Result &type) { type.metaObject = 0; type.type = -1; @@ -1652,7 +1676,7 @@ bool QmlBindingCompiler::parseConstant(QmlJS::AST::Node *node, Result &type) } } -bool QmlBindingCompiler::buildName(QStringList &name, +bool QmlBindingCompilerPrivate::buildName(QStringList &name, QmlJS::AST::Node *node) { if (node->kind == AST::Node::Kind_IdentifierExpression) { @@ -1673,7 +1697,7 @@ bool QmlBindingCompiler::buildName(QStringList &name, } -bool QmlBindingCompiler::fetch(Result &rv, const QMetaObject *mo, int reg, int idx, const QStringList &subName) +bool QmlBindingCompilerPrivate::fetch(Result &rv, const QMetaObject *mo, int reg, int idx, const QStringList &subName) { QMetaProperty prop = mo->property(idx); rv.metaObject = 0; @@ -1731,12 +1755,12 @@ bool QmlBindingCompiler::fetch(Result &rv, const QMetaObject *mo, int reg, int i return true; } -void QmlBindingCompiler::registerCleanup(int reg, int cleanup, int cleanupType) +void QmlBindingCompilerPrivate::registerCleanup(int reg, int cleanup, int cleanupType) { registerCleanups.insert(reg, qMakePair(cleanup, cleanupType)); } -int QmlBindingCompiler::acquireReg(int cleanup, int cleanupType) +int QmlBindingCompilerPrivate::acquireReg(int cleanup, int cleanupType) { for (int ii = 0; ii < 32; ++ii) { if (!(registers & (1 << ii))) { @@ -1751,7 +1775,7 @@ int QmlBindingCompiler::acquireReg(int cleanup, int cleanupType) return -1; } -void QmlBindingCompiler::releaseReg(int reg) +void QmlBindingCompilerPrivate::releaseReg(int reg) { Q_ASSERT(reg >= 0 && reg <= 31); @@ -1769,7 +1793,7 @@ void QmlBindingCompiler::releaseReg(int reg) registers &= ~mask; } -int QmlBindingCompiler::registerString(const QString &string) +int QmlBindingCompilerPrivate::registerString(const QString &string) { Q_ASSERT(!string.isEmpty()); @@ -1788,7 +1812,7 @@ int QmlBindingCompiler::registerString(const QString &string) return strings - 1; } -bool QmlBindingCompiler::subscription(const QStringList &sub, Result *result) +bool QmlBindingCompilerPrivate::subscription(const QStringList &sub, Result *result) { QString str = sub.join(QLatin1String(".")); result->subscriptionSet.insert(str); @@ -1801,12 +1825,13 @@ bool QmlBindingCompiler::subscription(const QStringList &sub, Result *result) } } -int QmlBindingCompiler::subscriptionIndex(const QStringList &sub) +int QmlBindingCompilerPrivate::subscriptionIndex(const QStringList &sub) { QString str = sub.join(QLatin1String(".")); QHash<QString, int>::ConstIterator iter = subscriptionIds.find(str); if (iter == subscriptionIds.end()) iter = subscriptionIds.insert(str, subscriptionIds.count()); + usedSubscriptionIds.insert(*iter); return *iter; } @@ -1814,7 +1839,7 @@ int QmlBindingCompiler::subscriptionIndex(const QStringList &sub) Returns true if lhs contains no subscriptions that aren't also in base or rhs AND rhs contains no subscriptions that aren't also in base or lhs. */ -bool QmlBindingCompiler::subscriptionNeutral(const QSet<QString> &base, +bool QmlBindingCompilerPrivate::subscriptionNeutral(const QSet<QString> &base, const QSet<QString> &lhs, const QSet<QString> &rhs) { @@ -1828,5 +1853,118 @@ bool QmlBindingCompiler::subscriptionNeutral(const QSet<QString> &base, return difflhs.isEmpty(); } + + +QmlBindingCompiler::QmlBindingCompiler() +: d(new QmlBindingCompilerPrivate) +{ +} + +QmlBindingCompiler::~QmlBindingCompiler() +{ + delete d; d = 0; +} + +/* +Returns true if any bindings were compiled. +*/ +bool QmlBindingCompiler::isValid() const +{ + return d->bytecode.count(); +} + +/* +-1 on failure, otherwise the binding index to use. +*/ +int QmlBindingCompiler::compile(const QmlBasicScript::Expression &expression, + QmlEnginePrivate *engine) +{ + if (!expression.expression.asAST()) return false; + + d->context = expression.context; + d->component = expression.component; + d->destination = expression.property; + d->ids = expression.ids; + d->imports = expression.imports; + d->engine = engine; + + if (d->compile(expression.expression.asAST())) { + return d->commitCompile(); + } else { + return -1; + } +} + + +QByteArray QmlBindingCompilerPrivate::buildSignalTable() const +{ + QHash<int, QList<int> > table; + + for (int ii = 0; ii < committed.count(); ++ii) { + const QSet<int> &deps = committed.dependencies.at(ii); + for (QSet<int>::ConstIterator iter = deps.begin(); iter != deps.end(); ++iter) + table[*iter].append(ii); + } + + QVector<quint32> header; + QVector<quint32> data; + for (int ii = 0; ii < committed.subscriptionIds.count(); ++ii) { + header.append(committed.subscriptionIds.count() + data.count()); + const QList<int> &bindings = table[ii]; + data.append(bindings.count()); + for (int jj = 0; jj < bindings.count(); ++jj) + data.append(bindings.at(jj)); + } + header << data; + + return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32)); +} + +/* +Returns the compiled program. +*/ +QByteArray QmlBindingCompiler::program() const +{ + QByteArray programData; + + if (isValid()) { + Program prog; + prog.bindings = d->committed.count(); + + QVector<Instr> bytecode; + Instr skip; + skip.type = Instr::Skip; + skip.skip.reg = -1; + for (int ii = 0; ii < d->committed.count(); ++ii) { + skip.skip.count = d->committed.count() - ii - 1; + skip.skip.count+= d->committed.offsets.at(ii); + bytecode << skip; + } + bytecode << d->committed.bytecode; + + QByteArray data = d->committed.data; + while (data.count() % 4) data.append('\0'); + prog.signalTableOffset = data.count(); + data += d->buildSignalTable(); + + prog.dataLength = 4 * ((data.size() + 3) / 4); + prog.subscriptions = d->committed.subscriptionIds.count(); + prog.identifiers = d->committed.strings; + int size = sizeof(Program) + bytecode.count() * sizeof(Instr); + size += prog.dataLength; + + programData.resize(size); + memcpy(programData.data(), &prog, sizeof(Program)); + if (prog.dataLength) + memcpy((char *)((Program *)programData.data())->data(), data.constData(), + data.size()); + memcpy((char *)((Program *)programData.data())->instructions(), bytecode.constData(), + bytecode.count() * sizeof(Instr)); + } + + return programData; +} + + QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlbindingvme_p.h b/src/declarative/qml/qmlbindingvme_p.h index a01c308..de73037 100644 --- a/src/declarative/qml/qmlbindingvme_p.h +++ b/src/declarative/qml/qmlbindingvme_p.h @@ -80,13 +80,34 @@ public: QScriptDeclarativeClass::PersistentIdentifier *identifiers; }; - static QByteArray compile(const QmlBasicScript::Expression &, QmlEnginePrivate *); - static void run(const char *program, + static void init(const char *program, Config *config, + quint32 **sigTable, quint32 *bindingCount); + static void run(const char *program, int instr, Config *config, QmlContextPrivate *context, QObject **scopes, QObject **outputs); static void dump(const QByteArray &); }; +class QmlBindingCompilerPrivate; +class QmlBindingCompiler +{ +public: + QmlBindingCompiler(); + ~QmlBindingCompiler(); + + // Returns true if bindings were compiled + bool isValid() const; + + // -1 on failure, otherwise the binding index to use + int compile(const QmlBasicScript::Expression &, QmlEnginePrivate *); + + // Returns the compiled program + QByteArray program() const; + +private: + QmlBindingCompilerPrivate *d; +}; + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index 53ea18e..f25f8b8 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -643,6 +643,10 @@ void QmlCompiler::compileTree(Object *tree) init.init.bindingsSize = compileState.bindings.count(); init.init.parserStatusSize = compileState.parserStatusCount; init.init.contextCache = genContextCache(); + if (compileState.compiledBindingData.isEmpty()) + init.init.compiledBinding = -1; + else + init.init.compiledBinding = output->indexForByteArray(compileState.compiledBindingData); output->bytecode << init; genObject(tree); @@ -1010,6 +1014,10 @@ void QmlCompiler::genComponent(QmlParser::Object *obj) init.init.bindingsSize = compileState.bindings.count(); init.init.parserStatusSize = compileState.parserStatusCount; init.init.contextCache = genContextCache(); + if (compileState.compiledBindingData.isEmpty()) + init.init.compiledBinding = -1; + else + init.init.compiledBinding = output->indexForByteArray(compileState.compiledBindingData); init.line = obj->location.start.line; output->bytecode << init; @@ -2423,7 +2431,7 @@ void QmlCompiler::genBindingAssignment(QmlParser::Value *binding, if (ref.dataType == BindingReference::Experimental) { QmlInstruction store; store.type = QmlInstruction::StoreCompiledBinding; - store.assignBinding.value = output->indexForByteArray(ref.compiledData); + store.assignBinding.value = ref.compiledIndex; store.assignBinding.context = ref.bindingContext.stack; store.assignBinding.owner = ref.bindingContext.owner; if (valueTypeProperty) @@ -2529,6 +2537,8 @@ bool QmlCompiler::completeComponentBuild() expr.component = compileState.root; expr.ids = compileState.ids; + QmlBindingCompiler bindingCompiler; + for (QHash<QmlParser::Value*,BindingReference>::Iterator iter = compileState.bindings.begin(); iter != compileState.bindings.end(); ++iter) { BindingReference &binding = *iter; @@ -2541,15 +2551,15 @@ bool QmlCompiler::completeComponentBuild() bs.compile(expr); if (qmlExperimental() && (!bs.isValid() || (!bs.isSingleIdFetch() && !bs.isSingleContextProperty()))) { - - QByteArray qmvdata = QmlBindingVME::compile(expr, QmlEnginePrivate::get(engine)); - if (!qmvdata.isEmpty()) { - qWarning() << expr.expression.asScript(); - QmlBindingVME::dump(qmvdata); + int index = bindingCompiler.compile(expr, QmlEnginePrivate::get(engine)); + if (index != -1) { + qWarning() << "Accepted for optimization:" << qPrintable(expr.expression.asScript()); binding.dataType = BindingReference::Experimental; - binding.compiledData = qmvdata; + binding.compiledIndex = index; componentStat.optimizedBindings++; continue; + } else { + qWarning() << "Rejected for optimization:" << qPrintable(expr.expression.asScript()); } } @@ -2599,6 +2609,11 @@ bool QmlCompiler::completeComponentBuild() sizeof(quint32))); } + if (bindingCompiler.isValid()) { + compileState.compiledBindingData = bindingCompiler.program(); + QmlBindingVME::dump(compileState.compiledBindingData); + } + saveComponentState(); return true; diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h index d734a12..3fcba15 100644 --- a/src/declarative/qml/qmlcompiler_p.h +++ b/src/declarative/qml/qmlcompiler_p.h @@ -273,6 +273,8 @@ private: enum DataType { QtScript, BasicScript, Experimental }; DataType dataType; + int compiledIndex; + QByteArray compiledData; BindingContext bindingContext; }; @@ -287,6 +289,8 @@ private: int parserStatusCount; int pushedProperties; + QByteArray compiledBindingData; + QHash<QmlParser::Value *, BindingReference> bindings; QList<QmlParser::Object *> aliasingObjects; QmlParser::Object *root; diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index ad68c8f..6b66095 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -564,7 +564,7 @@ void QmlDeclarativeData::destroyed(QObject * /*object*/) QmlAbstractBinding *next = binding->m_nextBinding; binding->m_prevBinding = 0; binding->m_nextBinding = 0; - delete binding; + binding->destroy(); binding = next; } diff --git a/src/declarative/qml/qmlinstruction_p.h b/src/declarative/qml/qmlinstruction_p.h index d06ac86..6e2b452 100644 --- a/src/declarative/qml/qmlinstruction_p.h +++ b/src/declarative/qml/qmlinstruction_p.h @@ -170,6 +170,7 @@ public: int bindingsSize; int parserStatusSize; int contextCache; + int compiledBinding; } init; struct { int type; diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index e4295a1..ae7bf51 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -161,6 +161,8 @@ QObject *QmlVME::run(QmlVMEStack<QObject *> &stack, QmlContext *ctxt, int status = -1; //for dbus QmlMetaProperty::WriteFlags flags = QmlMetaProperty::BypassInterceptor; + QmlOptimizedBindings *optimizedBindings = 0; + for (int ii = start; !isError() && ii < (start + count); ++ii) { const QmlInstruction &instr = comp->bytecode.at(ii); @@ -171,9 +173,10 @@ QObject *QmlVME::run(QmlVMEStack<QObject *> &stack, QmlContext *ctxt, bindValues = QmlEnginePrivate::SimpleList<QmlAbstractBinding>(instr.init.bindingsSize); if (instr.init.parserStatusSize) parserStatus = QmlEnginePrivate::SimpleList<QmlParserStatus>(instr.init.parserStatusSize); - if (instr.init.contextCache != -1) cp->setIdPropertyData(comp->contextCaches.at(instr.init.contextCache)); + if (instr.init.compiledBinding != -1) + optimizedBindings = new QmlOptimizedBindings(datas.at(instr.init.compiledBinding).constData(), ctxt); } break; @@ -619,13 +622,11 @@ QObject *QmlVME::run(QmlVMEStack<QObject *> &stack, QmlContext *ctxt, if (stack.count() == 1 && bindingSkipList.testBit(property & 0xFFFF)) break; - const char *data = datas.at(instr.assignBinding.value).constData(); - - QmlBinding_Basic *bind = - new QmlBinding_Basic(target, property, data, comp, scope, ctxt); - bindValues.append(bind); - bind->m_mePtr = &bindValues.values[bindValues.count - 1]; - bind->addToObject(target); + QmlAbstractBinding *binding = + optimizedBindings->configBinding(instr.assignBinding.value, target, scope, property); + bindValues.append(binding); + binding->m_mePtr = &bindValues.values[bindValues.count - 1]; + binding->addToObject(target); } break; @@ -894,6 +895,11 @@ QObject *QmlVME::run(QmlVMEStack<QObject *> &stack, QmlContext *ctxt, } } + if (optimizedBindings) { + optimizedBindings->release(); + optimizedBindings = 0; + } + if (isError()) { if (!stack.isEmpty()) { delete stack.at(0); |