From 01a946c6b3838a1c094687785be041b156ac1410 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Wed, 27 Jan 2010 13:32:24 +1000 Subject: Cleanup --- src/declarative/qml/qml.pri | 6 +- src/declarative/qml/qmlbindingoptimizations.cpp | 173 -- src/declarative/qml/qmlbindingoptimizations_p.h | 109 - src/declarative/qml/qmlbindingvme.cpp | 2517 --------------------- src/declarative/qml/qmlbindingvme_p.h | 182 -- src/declarative/qml/qmlcompiledbindings.cpp | 2749 +++++++++++++++++++++++ src/declarative/qml/qmlcompiledbindings_p.h | 116 + src/declarative/qml/qmlcompiler.cpp | 4 +- src/declarative/qml/qmlcontext.cpp | 4 +- src/declarative/qml/qmlcontext_p.h | 62 +- src/declarative/qml/qmlvme.cpp | 4 +- 11 files changed, 2926 insertions(+), 3000 deletions(-) delete mode 100644 src/declarative/qml/qmlbindingoptimizations.cpp delete mode 100644 src/declarative/qml/qmlbindingoptimizations_p.h delete mode 100644 src/declarative/qml/qmlbindingvme.cpp delete mode 100644 src/declarative/qml/qmlbindingvme_p.h create mode 100644 src/declarative/qml/qmlcompiledbindings.cpp create mode 100644 src/declarative/qml/qmlcompiledbindings_p.h diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index 9268545..6f2f57f 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -32,9 +32,8 @@ SOURCES += \ $$PWD/qmlscriptparser.cpp \ $$PWD/qmlenginedebug.cpp \ $$PWD/qmlrewrite.cpp \ - $$PWD/qmlbindingvme.cpp \ $$PWD/qmlvaluetype.cpp \ - $$PWD/qmlbindingoptimizations.cpp \ + $$PWD/qmlcompiledbindings.cpp \ $$PWD/qmlxmlhttprequest.cpp \ $$PWD/qmlsqldatabase.cpp \ $$PWD/qmetaobjectbuilder.cpp \ @@ -95,13 +94,12 @@ HEADERS += \ $$PWD/qmldeclarativedata_p.h \ $$PWD/qmlerror.h \ $$PWD/qmlscriptparser_p.h \ - $$PWD/qmlbindingvme_p.h \ $$PWD/qmlenginedebug_p.h \ $$PWD/qmlrewrite_p.h \ $$PWD/qpodvector_p.h \ $$PWD/qbitfield_p.h \ $$PWD/qmlvaluetype_p.h \ - $$PWD/qmlbindingoptimizations_p.h \ + $$PWD/qmlcompiledbindings_p.h \ $$PWD/qmlxmlhttprequest_p.h \ $$PWD/qmlsqldatabase_p.h \ $$PWD/qmetaobjectbuilder_p.h \ diff --git a/src/declarative/qml/qmlbindingoptimizations.cpp b/src/declarative/qml/qmlbindingoptimizations.cpp deleted file mode 100644 index 1e49636..0000000 --- a/src/declarative/qml/qmlbindingoptimizations.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qmlbindingoptimizations_p.h" - -#include "qmlcontext_p.h" -#include -#include "qmlbindingvme_p.h" - -QT_BEGIN_NAMESPACE - -int QmlOptimizedBindings::methodCount = -1; - -QmlOptimizedBindings::QmlOptimizedBindings(const char *program, QmlContext *context) -: m_program(program) -{ - if (methodCount == -1) - methodCount = QmlOptimizedBindings::staticMetaObject.methodCount(); - - m_config.target = this; - 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); -} - -QmlOptimizedBindings::~QmlOptimizedBindings() -{ - delete [] m_bindings; -} - -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(target); - update(flags); - } else { - removeFromObject(); - } - - QmlAbstractBinding::setEnabled(e, flags); - - if (enabled != e) { - enabled = e; - - if (e) update(flags); - } -} - -int QmlOptimizedBindings::Binding::propertyIndex() -{ - return property & 0xFFFF; -} - -void QmlOptimizedBindings::Binding::update(QmlMetaProperty::WriteFlags) -{ - parent->run(this); -} - -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) { - qWarning("QmlOptimizedBindings: Attempted to evaluate an expression in an invalid context"); - return; - } - QmlContextPrivate *cp = QmlContextPrivate::get(context); - - if (binding->property & 0xFFFF0000) { - QmlEnginePrivate *ep = QmlEnginePrivate::get(cp->engine); - - QmlValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF]; - Q_ASSERT(vt); - vt->read(binding->target, binding->property & 0xFFFF); - - QObject *target = vt; - QmlBindingVME::run(m_program, binding->index, &m_config, cp, binding, - binding->scope, target); - - vt->write(binding->target, binding->property & 0xFFFF, - QmlMetaProperty::DontRemoveBinding); - } else { - QmlBindingVME::run(m_program, binding->index, &m_config, cp, binding, - binding->scope, binding->target); - } -} - -QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlbindingoptimizations_p.h b/src/declarative/qml/qmlbindingoptimizations_p.h deleted file mode 100644 index 42b7d17..0000000 --- a/src/declarative/qml/qmlbindingoptimizations_p.h +++ /dev/null @@ -1,109 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QMLBINDINGOPTIMIZATIONS_P_H -#define QMLBINDINGOPTIMIZATIONS_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 "qmlexpression_p.h" -#include "qmlbinding.h" -#include "qmlbindingvme_p.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -class QmlOptimizedBindings : public QObject, public QmlAbstractExpression, public QmlRefCount -{ -public: - QmlOptimizedBindings(const char *program, QmlContext *context); - virtual ~QmlOptimizedBindings(); - QmlAbstractBinding *configBinding(int index, QObject *target, QObject *scope, int property); - -protected: - int qt_metacall(QMetaObject::Call, int, void **); - -private: - struct Binding : public QmlAbstractBinding, public QmlDelayedError { - 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; - const char *m_program; - Binding *m_bindings; - quint32 *m_signalTable; - - static int methodCount; -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // QMLBINDINGOPTIMIZATIONS_P_H - diff --git a/src/declarative/qml/qmlbindingvme.cpp b/src/declarative/qml/qmlbindingvme.cpp deleted file mode 100644 index 47f02b2..0000000 --- a/src/declarative/qml/qmlbindingvme.cpp +++ /dev/null @@ -1,2517 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qmlbindingvme_p.h" -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -using namespace QmlJS; - -QmlBindingVME::Config::Subscription::Signal *QmlBindingVME::Config::Subscription::signal() -{ - if (type == IdType) ((Id *)idData)->~Id(); - if (type != SignalType) new (signalData) Signal; - type = SignalType; - return (Signal *)signalData; -} - -QmlBindingVME::Config::Subscription::Id *QmlBindingVME::Config::Subscription::id() -{ - if (type == SignalType) ((Signal *)signalData)->~Signal(); - if (type != IdType) new (idData) Id; - type = IdType; - return (Id *)idData; -} - -namespace { -// Supported types: int, qreal, QString (needs constr/destr), QObject*, bool -struct Register { - void setUndefined() { type = 0; } - void setUnknownButDefined() { type = -1; } - void setNaN() { setqreal(qSNaN()); } - bool isUndefined() const { return type == 0; } - - void setQObject(QObject *o) { *((QObject **)data) = o; type = QMetaType::QObjectStar; } - QObject *getQObject() const { return *((QObject **)data); } - - void setqreal(qreal v) { *((qreal *)data) = v; type = QMetaType::QReal; } - qreal getqreal() const { return *((qreal *)data); } - - void setint(int v) { *((int *)data) = v; type = QMetaType::Int; } - int getint() const { return *((int *)data); } - - void setbool(bool v) { *((bool *)data) = v; type = QMetaType::Bool; } - bool getbool() const { return *((bool *)data); } - - QVariant *getvariantptr() { return (QVariant *)typeDataPtr(); } - QString *getstringptr() { return (QString *)typeDataPtr(); } - QUrl *geturlptr() { return (QUrl *)typeDataPtr(); } - const QVariant *getvariantptr() const { return (QVariant *)typeDataPtr(); } - const QString *getstringptr() const { return (QString *)typeDataPtr(); } - const QUrl *geturlptr() const { return (QUrl *)typeDataPtr(); } - - void *typeDataPtr() { return (void *)&data; } - void *typeMemory() { return (void *)data; } - const void *typeDataPtr() const { return (void *)&data; } - const void *typeMemory() const { return (void *)data; } - - int gettype() const { return type; } - void settype(int t) { type = t; } - - int type; // Optional type - void *data[2]; // Object stored here -}; - -// This structure is exactly 8-bytes in size -struct Instr { - enum { - Noop, - - Subscribe, // subscribe - SubscribeId, // subscribe - - LoadId, // load - LoadScope, // load - LoadRoot, // load - LoadAttached, // attached - - ConvertIntToReal, // unaryop - ConvertRealToInt, // unaryop - - Real, // real_value - Int, // int_value - Bool, // bool_value - String, // string_value - - AddReal, // binaryop - AddInt, // binaryop - AddString, // binaryop - - MinusReal, // binaryop - MinusInt, // binaryop - - CompareReal, // binaryop - CompareString, // binaryop - - NotCompareReal, // binaryop - NotCompareString, // binaryop - - GreaterThanReal, // binaryop - MaxReal, // binaryop - MinReal, // binaryop - - NewString, // construct - NewUrl, // construct - - CleanupUrl, // cleanup - CleanupString, // cleanup - - Copy, // copy - Fetch, // fetch - Store, // store - - Skip, // skip - - Done, - - // Speculative property resolution - InitString, // initstring - FindGeneric, // find - FindGenericTerminal, // find - FindProperty, // find - FindPropertyTerminal, // find - CleanupGeneric, // cleanup - ConvertGenericToReal, // unaryop - ConvertGenericToBool, // unaryop - ConvertGenericToString, // unaryop - ConvertGenericToUrl, // unaryop - }; - - union { - struct { - quint8 type; - quint8 packing[7]; - } common; - struct { - quint8 type; - quint8 packing[3]; - quint16 subscriptions; - quint16 identifiers; - } init; - struct { - quint8 type; - qint8 reg; - quint16 offset; - quint32 index; - } subscribe; - struct { - quint8 type; - qint8 reg; - quint8 packing[2]; - quint32 index; - } load; - struct { - quint8 type; - qint8 output; - qint8 reg; - quint8 exceptionId; - quint32 index; - } attached; - struct { - quint8 type; - qint8 output; - qint8 reg; - quint8 exceptionId; - quint32 index; - } store; - struct { - quint8 type; - qint8 output; - qint8 objectReg; - quint8 exceptionId; - quint32 index; - } fetch; - struct { - quint8 type; - qint8 reg; - qint8 src; - quint8 packing[5]; - } copy; - struct { - quint8 type; - qint8 reg; - quint8 packing[6]; - } construct; - struct { - quint8 type; - qint8 reg; - quint8 packing[2]; - float value; - } real_value; - struct { - quint8 type; - qint8 reg; - quint8 packing[2]; - int value; - } int_value; - struct { - quint8 type; - qint8 reg; - bool value; - quint8 packing[5]; - } bool_value; - struct { - quint8 type; - qint8 reg; - quint16 length; - quint32 offset; - } string_value; - struct { - quint8 type; - qint8 output; - qint8 src1; - qint8 src2; - quint8 packing[4]; - } binaryop; - struct { - quint8 type; - qint8 output; - qint8 src; - quint8 packing[5]; - } unaryop; - struct { - quint8 type; - qint8 reg; - quint8 packing[2]; - quint32 count; - } skip; - struct { - quint8 type; - qint8 reg; - qint8 src; - quint8 exceptionId; - quint16 name; - quint16 subscribeIndex; - } find; - struct { - quint8 type; - qint8 reg; - quint8 packing[6]; - } cleanup; - struct { - quint8 type; - quint8 packing[1]; - quint16 offset; - quint32 dataIdx; - } initstring; - }; -}; - -struct Program { - quint32 bindings; - quint32 dataLength; - quint32 signalTableOffset; - quint32 exceptionDataOffset; - quint16 subscriptions; - quint16 identifiers; - quint16 instructionCount; - quint16 dummy; - - const char *data() const { return ((const char *)this) + sizeof(Program); } - const Instr *instructions() const { return (const Instr *)(data() + dataLength); } -}; -} - -struct QmlBindingCompilerPrivate -{ - struct Result { - Result() : unknownType(false), metaObject(0), type(-1), reg(-1) {} - bool operator==(const Result &o) const { - return unknownType == o.unknownType && - metaObject == o.metaObject && - type == o.type && - reg == o.reg; - } - bool operator!=(const Result &o) const { - return !(*this == o); - } - bool unknownType; - const QMetaObject *metaObject; - int type; - int reg; - - QSet subscriptionSet; - }; - - QmlBindingCompilerPrivate() : registers(0) {} - - void resetInstanceState(); - int commitCompile(); - - QmlParser::Object *context; - QmlParser::Object *component; - QmlParser::Property *destination; - QHash ids; - 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 &); - - bool tryName(QmlJS::AST::Node *); - bool parseName(QmlJS::AST::Node *, Result &); - - bool tryArith(QmlJS::AST::Node *); - bool parseArith(QmlJS::AST::Node *, Result &); - bool numberArith(Result &, const Result &, const Result &, QSOperator::Op op); - bool stringArith(Result &, const Result &, const Result &, QSOperator::Op op); - - bool tryLogic(QmlJS::AST::Node *); - bool parseLogic(QmlJS::AST::Node *, Result &); - - bool tryConditional(QmlJS::AST::Node *); - bool parseConditional(QmlJS::AST::Node *, Result &); - - bool tryConstant(QmlJS::AST::Node *); - bool parseConstant(QmlJS::AST::Node *, Result &); - - bool tryMethod(QmlJS::AST::Node *); - bool parseMethod(QmlJS::AST::Node *, Result &); - - bool buildName(QStringList &, QmlJS::AST::Node *, QList *nodes = 0); - bool fetch(Result &type, const QMetaObject *, int reg, int idx, const QStringList &, QmlJS::AST::ExpressionNode *); - - quint32 registers; - QHash > registerCleanups; - int acquireReg(int cleanup = Instr::Noop, int cleanupType = 0); - void registerCleanup(int reg, int cleanup, int cleanupType = 0); - void releaseReg(int); - - int registerLiteralString(const QString &); - int registerString(const QString &); - QHash > registeredStrings; - QByteArray data; - - bool subscription(const QStringList &, Result *); - int subscriptionIndex(const QStringList &); - bool subscriptionNeutral(const QSet &base, const QSet &lhs, const QSet &rhs); - - quint8 exceptionId(QmlJS::AST::ExpressionNode *); - QVector exceptions; - - QSet usedSubscriptionIds; - QSet subscriptionSet; - QHash subscriptionIds; - QVector bytecode; - - // Committed binding data - struct { - QList offsets; - QList > dependencies; - - QVector bytecode; - QByteArray data; - QHash subscriptionIds; - QVector exceptions; - - QHash > registeredStrings; - - int count() const { return offsets.count(); } - } committed; - - QByteArray buildSignalTable() const; - QByteArray buildExceptionData() const; -}; - -inline void unsubscribe(int subIndex, QmlBindingVME::Config *config) -{ - QmlBindingVME::Config::Subscription *sub = (config->subscriptions + subIndex); - if (sub->isSignal()) { - QmlBindingVME::Config::Subscription::Signal *s = sub->signal(); - if (s->source) -#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 2)) - QMetaObject::disconnectOne(s->source, s->notifyIndex, - config->target, config->targetSlot + subIndex); -#else - // QTBUG-6781 - QMetaObject::disconnect(s->source, s->notifyIndex, - config->target, config->targetSlot + subIndex); -#endif - } else if (sub->isId()) { - sub->id()->reset(); - } -} - -inline void subscribeId(QmlContextPrivate *p, int idIndex, - int subIndex, QmlBindingVME::Config *config) -{ - unsubscribe(subIndex, config); - - if (p->idValues[idIndex]) { - QmlBindingVME::Config::Subscription *sub = (config->subscriptions + subIndex); - QmlBindingVME::Config::Subscription::Id *i = sub->id(); - - i->next = p->idValues[idIndex].bindings; - i->prev = &p->idValues[idIndex].bindings; - p->idValues[idIndex].bindings = i; - if (i->next) i->next->prev = &i->next; - - i->target = config->target; - i->methodIndex = config->targetSlot + subIndex; - } -} - -inline void subscribe(QObject *o, int notifyIndex, - int subIndex, QmlBindingVME::Config *config) -{ - QmlBindingVME::Config::Subscription *sub = (config->subscriptions + subIndex); - - if (sub->isId()) - unsubscribe(subIndex, config); - - QmlBindingVME::Config::Subscription::Signal *s = sub->signal(); - if (o != s->source || notifyIndex != s->notifyIndex) { - if (s->source) -#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 2)) - QMetaObject::disconnectOne(s->source, s->notifyIndex, - config->target, config->targetSlot + subIndex); -#else - // QTBUG-6781 - QMetaObject::disconnect(s->source, s->notifyIndex, - config->target, config->targetSlot + subIndex); -#endif - s->source = o; - s->notifyIndex = notifyIndex; - if (s->source && s->notifyIndex != -1) - QMetaObject::connect(s->source, s->notifyIndex, config->target, - config->targetSlot + subIndex, Qt::DirectConnection); - } -} - -// Conversion functions - these MUST match the QtScript expression path -inline static qreal toReal(Register *reg, int type, bool *ok = 0) -{ - if (ok) *ok = true; - - if (type == QMetaType::QReal) { - return reg->getqreal(); - } else if (type == qMetaTypeId()) { - return reg->getvariantptr()->toReal(); - } else { - if (ok) *ok = false; - return 0; - } -} - -inline static QString toString(Register *reg, int type, bool *ok = 0) -{ - if (ok) *ok = true; - - if (type == QMetaType::QReal) { - return QString::number(reg->getqreal()); - } else if (type == QMetaType::Int) { - return QString::number(reg->getint()); - } else if (type == qMetaTypeId()) { - return reg->getvariantptr()->toString(); - } else if (type == QMetaType::QString) { - return *reg->getstringptr(); - } else { - if (ok) *ok = false; - return QString(); - } -} - -inline static bool toBool(Register *reg, int type, bool *ok = 0) -{ - if (ok) *ok = true; - - if (type == QMetaType::Bool) { - return reg->getbool(); - } else if (type == qMetaTypeId()) { - return reg->getvariantptr()->toBool(); - } else { - if (ok) *ok = false; - return false; - } -} - -inline static QUrl toUrl(Register *reg, int type, QmlContextPrivate *context, bool *ok = 0) -{ - if (ok) *ok = true; - - QUrl base; - if (type == qMetaTypeId()) { - QVariant *var = reg->getvariantptr(); - int vt = var->type(); - if (vt == QVariant::Url) { - base = var->toUrl(); - } else if (vt == QVariant::ByteArray) { - base = QUrl(QString::fromUtf8(var->toByteArray())); - } else if (vt == QVariant::String) { - base = QUrl(var->toString()); - } else { - if (ok) *ok = false; - return QUrl(); - } - } else if (type == QMetaType::QString) { - base = QUrl(*reg->getstringptr()); - } else { - if (ok) *ok = false; - return QUrl(); - } - - if (!base.isEmpty() && base.isRelative()) - return context->url.resolved(base); - else - return base; -} - -static QObject *variantToQObject(const QVariant &value, bool *ok) -{ - if (ok) *ok = true; - - if (value.userType() == QMetaType::QObjectStar) { - return qvariant_cast(value); - } else { - if (ok) *ok = false; - return 0; - } -} - -static QmlPropertyCache::Data *findproperty(QObject *obj, - const QScriptDeclarativeClass::Identifier &name, - QmlEnginePrivate *enginePriv, - QmlPropertyCache::Data &local) -{ - QmlPropertyCache *cache = 0; - QmlDeclarativeData *ddata = QmlDeclarativeData::get(obj); - if (ddata) - cache = ddata->propertyCache; - if (!cache) { - cache = enginePriv->cache(obj); - if (cache && ddata) { cache->addref(); ddata->propertyCache = cache; } - } - - QmlPropertyCache::Data *property = 0; - - if (cache) { - property = cache->property(name); - } else { - qWarning() << "QmlBindingVME: Slow search" << enginePriv->objectClass->toString(name); - local = QmlPropertyCache::create(obj->metaObject(), - enginePriv->objectClass->toString(name)); - if (local.isValid()) - property = &local; - } - - return property; -} - -static bool findproperty(QObject *obj, Register *output, - QmlEnginePrivate *enginePriv, - QmlBindingVME::Config *config, int subIdx, - const QScriptDeclarativeClass::Identifier &name, - bool isTerminal) -{ - if (!obj) { - output->setUndefined(); - return false; - } - - QmlPropertyCache::Data local; - QmlPropertyCache::Data *property = findproperty(obj, name, enginePriv, local); - - if (property) { - if (subIdx != -1) - subscribe(obj, property->notifyIndex, subIdx, config); - - if (property->flags & QmlPropertyCache::Data::IsQObjectDerived) { - void *args[] = { output->typeDataPtr(), 0 }; - QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); - output->settype(QMetaType::QObjectStar); - } else if (property->propType == qMetaTypeId()) { - QVariant v; - void *args[] = { &v, 0 }; - QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); - - if (isTerminal) { - new (output->typeDataPtr()) QVariant(v); - output->settype(qMetaTypeId()); - } else { - bool ok; - output->setQObject(variantToQObject(v, &ok)); - if (!ok) - output->setUndefined(); - else - output->settype(QMetaType::QObjectStar); - } - - } else { - if (!isTerminal) { - output->setUndefined(); - } else if (property->propType == QMetaType::QReal) { - void *args[] = { output->typeDataPtr(), 0 }; - QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); - output->settype(QMetaType::QReal); - } else if (property->propType == QMetaType::Int) { - void *args[] = { output->typeDataPtr(), 0 }; - QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); - output->settype(QMetaType::Int); - } else if (property->propType == QMetaType::Bool) { - void *args[] = { output->typeDataPtr(), 0 }; - QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); - output->settype(QMetaType::Bool); - } else if (property->propType == QMetaType::QString) { - new (output->typeDataPtr()) QString(); - void *args[] = { output->typeDataPtr(), 0 }; - QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); - output->settype(QMetaType::QString); - } else { - new (output->typeDataPtr()) - QVariant(obj->metaObject()->property(property->coreIndex).read(obj)); - output->settype(qMetaTypeId()); - } - } - - return true; - } else { - output->setUndefined(); - return false; - } -} - -static void findgeneric(Register *output, // value output - QmlBindingVME::Config *config, - int subIdx, // Subscription index in config - QmlContextPrivate *context, // Context to search in - const QScriptDeclarativeClass::Identifier &name, - bool isTerminal) -{ - QmlEnginePrivate *enginePriv = QmlEnginePrivate::get(context->engine); - - while (context) { - - int contextPropertyIndex = context->propertyNames?context->propertyNames->value(name):-1; - - - if (contextPropertyIndex != -1) { - - if (subIdx != -1) - subscribe(QmlContextPrivate::get(context), contextPropertyIndex + context->notifyIndex, - subIdx, config); - - if (contextPropertyIndex < context->idValueCount) { - output->setQObject(context->idValues[contextPropertyIndex]); - output->settype(QMetaType::QObjectStar); - } else { - const QVariant &value = context->propertyValues.at(contextPropertyIndex); - if (isTerminal) { - new (output->typeDataPtr()) QVariant(value); - output->settype(qMetaTypeId()); - } else { - bool ok; - output->setQObject(variantToQObject(value, &ok)); - if (!ok) { output->setUndefined(); } - else { output->settype(QMetaType::QObjectStar); } - return; - } - } - - return; - } - - for (int ii = 0; ii < context->scripts.count(); ++ii) { - QScriptValue function = QScriptDeclarativeClass::function(context->scripts.at(ii), name); - if (function.isValid()) { - qFatal("Binding optimizer resolved name to QScript method"); - } - } - - if (QObject *root = context->defaultObjects.isEmpty()?0:context->defaultObjects.first()) { - - if (findproperty(root, output, enginePriv, config, subIdx, name, isTerminal)) - return; - - } - - if (context->parent) { - context = QmlContextPrivate::get(context->parent); - } else { - context = 0; - } - } - - output->setUndefined(); -} - -/*! -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; -} - -static void throwException(int id, QmlDelayedError *error, - Program *program, QmlContextPrivate *context, - const QString &description = QString()) -{ - error->error.setUrl(context->url); - if (description.isEmpty()) - error->error.setDescription(QLatin1String("TypeError: Result of expression is not an object")); - else - error->error.setDescription(description); - if (id != 0xFF) { - quint64 e = *((quint64 *)(program->data() + program->exceptionDataOffset) + id); - error->error.setLine((e >> 32) & 0xFFFFFFFF); - error->error.setColumn(e & 0xFFFFFFFF); - } else { - error->error.setLine(-1); - error->error.setColumn(-1); - } - if (!context->engine || !error->addError(QmlEnginePrivate::get(context->engine))) - qWarning() << error->error; -} - -void QmlBindingVME::run(const char *programData, int instrIndex, - Config *config, QmlContextPrivate *context, QmlDelayedError *error, - QObject *scope, QObject *output) -{ - error->removeError(); - - Register registers[32]; - int storeFlags = 0; - - QmlEnginePrivate *engine = QmlEnginePrivate::get(context->engine); - Program *program = (Program *)programData; - const Instr *instr = program->instructions(); - instr += instrIndex; - const char *data = program->data(); - - while (instr) { - - switch (instr->common.type) { - case Instr::Noop: - break; - - case Instr::SubscribeId: - subscribeId(context, instr->subscribe.index, instr->subscribe.offset, config); - break; - - case Instr::Subscribe: - { - QObject *o = 0; - int notifyIndex = instr->subscribe.index; - - const Register &object = registers[instr->subscribe.reg]; - if (!object.isUndefined()) o = object.getQObject(); - subscribe(o, instr->subscribe.index, instr->subscribe.offset, config); - } - break; - - case Instr::LoadId: - registers[instr->load.reg].setQObject(context->idValues[instr->load.index].data()); - break; - - case Instr::LoadScope: - registers[instr->load.reg].setQObject(scope); - break; - - case Instr::LoadRoot: - registers[instr->load.reg].setQObject(context->defaultObjects.at(0)); - break; - - case Instr::LoadAttached: - { - const Register &input = registers[instr->attached.reg]; - Register &output = registers[instr->attached.output]; - if (input.isUndefined()) { - throwException(instr->attached.exceptionId, error, program, context); - return; - } - - QObject *object = registers[instr->attached.reg].getQObject(); - if (!object) { - output.setUndefined(); - } else { - QObject *attached = - qmlAttachedPropertiesObjectById(instr->attached.index, - registers[instr->attached.reg].getQObject(), - true); - Q_ASSERT(attached); - output.setQObject(attached); - } - } - break; - - case Instr::ConvertIntToReal: - { - const Register &input = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (input.isUndefined()) output.setUndefined(); - else output.setqreal(qreal(input.getint())); - } - break; - - case Instr::ConvertRealToInt: - { - const Register &input = registers[instr->unaryop.src]; - Register &output = registers[instr->unaryop.output]; - if (input.isUndefined()) output.setUndefined(); - else output.setint(int(input.getqreal())); - } - break; - - case Instr::Real: - registers[instr->real_value.reg].setqreal(instr->real_value.value); - break; - - case Instr::Int: - registers[instr->int_value.reg].setint(instr->int_value.value); - break; - - case Instr::Bool: - registers[instr->bool_value.reg].setbool(instr->bool_value.value); - break; - - case Instr::String: - { - Register &output = registers[instr->string_value.reg]; - new (output.getstringptr()) - QString((QChar *)(data + instr->string_value.offset), instr->string_value.length); - output.settype(QMetaType::QString); - } - break; - - case Instr::AddReal: - { - const Register &lhs = registers[instr->binaryop.src1]; - const Register &rhs = registers[instr->binaryop.src2]; - Register &output = registers[instr->binaryop.output]; - if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); - else output.setqreal(lhs.getqreal() + rhs.getqreal()); - } - break; - - case Instr::AddInt: - { - const Register &lhs = registers[instr->binaryop.src1]; - const Register &rhs = registers[instr->binaryop.src2]; - Register &output = registers[instr->binaryop.output]; - if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); - else output.setint(lhs.getint() + rhs.getint()); - } - break; - - case Instr::AddString: - { - const Register &lhs = registers[instr->binaryop.src1]; - const Register &rhs = registers[instr->binaryop.src2]; - Register &output = registers[instr->binaryop.output]; - if (lhs.isUndefined() && rhs.isUndefined()) { output.setNaN(); } - else { - if (lhs.isUndefined()) - new (output.getstringptr()) - QString(QLatin1String("undefined") + *registers[instr->binaryop.src2].getstringptr()); - else if (rhs.isUndefined()) - new (output.getstringptr()) - QString(*registers[instr->binaryop.src1].getstringptr() + QLatin1String("undefined")); - else - new (output.getstringptr()) - QString(*registers[instr->binaryop.src1].getstringptr() + - *registers[instr->binaryop.src2].getstringptr()); - output.settype(QMetaType::QString); - } - } - break; - - case Instr::MinusReal: - { - const Register &lhs = registers[instr->binaryop.src1]; - const Register &rhs = registers[instr->binaryop.src2]; - Register &output = registers[instr->binaryop.output]; - if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); - else output.setqreal(lhs.getqreal() - rhs.getqreal()); - } - break; - - case Instr::MinusInt: - { - const Register &lhs = registers[instr->binaryop.src1]; - const Register &rhs = registers[instr->binaryop.src2]; - Register &output = registers[instr->binaryop.output]; - if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); - else output.setint(lhs.getint() - rhs.getint()); - } - break; - - case Instr::CompareReal: - { - const Register &lhs = registers[instr->binaryop.src1]; - const Register &rhs = registers[instr->binaryop.src2]; - Register &output = registers[instr->binaryop.output]; - if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() == rhs.isUndefined()); - else output.setbool(lhs.getqreal() == rhs.getqreal()); - } - break; - - case Instr::CompareString: - { - const Register &lhs = registers[instr->binaryop.src1]; - const Register &rhs = registers[instr->binaryop.src2]; - Register &output = registers[instr->binaryop.output]; - if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() == rhs.isUndefined()); - else output.setbool(*lhs.getstringptr() == *rhs.getstringptr()); - } - break; - - case Instr::NotCompareReal: - { - const Register &lhs = registers[instr->binaryop.src1]; - const Register &rhs = registers[instr->binaryop.src2]; - Register &output = registers[instr->binaryop.output]; - if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() != rhs.isUndefined()); - else output.setbool(lhs.getqreal() != rhs.getqreal()); - } - break; - - case Instr::NotCompareString: - { - const Register &lhs = registers[instr->binaryop.src1]; - const Register &rhs = registers[instr->binaryop.src2]; - Register &output = registers[instr->binaryop.output]; - if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() != rhs.isUndefined()); - else output.setbool(*lhs.getstringptr() != *rhs.getstringptr()); - } - break; - - case Instr::GreaterThanReal: - { - const Register &lhs = registers[instr->binaryop.src1]; - const Register &rhs = registers[instr->binaryop.src2]; - Register &output = registers[instr->binaryop.output]; - if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(false); - else output.setbool(lhs.getqreal() > rhs.getqreal()); - } - break; - - case Instr::MaxReal: - { - const Register &lhs = registers[instr->binaryop.src1]; - const Register &rhs = registers[instr->binaryop.src2]; - Register &output = registers[instr->binaryop.output]; - if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); - else output.setqreal(qMax(lhs.getqreal(), rhs.getqreal())); - } - break; - - case Instr::MinReal: - { - const Register &lhs = registers[instr->binaryop.src1]; - const Register &rhs = registers[instr->binaryop.src2]; - Register &output = registers[instr->binaryop.output]; - if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); - else output.setqreal(qMin(lhs.getqreal(), rhs.getqreal())); - } - break; - - case Instr::NewString: - { - Register &output = registers[instr->construct.reg]; - new (output.getstringptr()) QString; - output.settype(QMetaType::QString); - } - break; - - case Instr::NewUrl: - { - Register &output = registers[instr->construct.reg]; - new (output.geturlptr()) QUrl; - output.settype(QMetaType::QUrl); - } - break; - - case Instr::CleanupString: - registers[instr->cleanup.reg].getstringptr()->~QString(); - break; - - case Instr::CleanupUrl: - registers[instr->cleanup.reg].geturlptr()->~QUrl(); - break; - - case Instr::Fetch: - { - const Register &input = registers[instr->fetch.objectReg]; - Register &output = registers[instr->fetch.output]; - - if (input.isUndefined()) { - throwException(instr->fetch.exceptionId, error, program, context); - return; - } - - QObject *object = input.getQObject(); - if (!object) { - output.setUndefined(); - } else { - void *argv[] = { output.typeDataPtr(), 0 }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, instr->fetch.index, argv); - } - } - break; - - case Instr::Store: - { - Register &data = registers[instr->store.reg]; - if (data.isUndefined()) { - throwException(instr->store.exceptionId, error, program, context, - QLatin1String("Unable to assign undefined value")); - return; - } - - int status = -1; - void *argv[] = { data.typeDataPtr(), 0, &status, &storeFlags }; - QMetaObject::metacall(output, QMetaObject::WriteProperty, - instr->store.index, argv); - } - break; - - case Instr::Copy: - registers[instr->copy.reg] = registers[instr->copy.src]; - break; - - case Instr::Skip: - if (instr->skip.reg == -1 || !registers[instr->skip.reg].getbool()) - instr += instr->skip.count; - break; - - case Instr::Done: - return; - - case Instr::InitString: - if (!config->identifiers[instr->initstring.offset].identifier) { - quint32 len = *(quint32 *)(data + instr->initstring.dataIdx); - QChar *strdata = (QChar *)(data + instr->initstring.dataIdx + sizeof(quint32)); - - QString str = QString::fromRawData(strdata, len); - - config->identifiers[instr->initstring.offset] = - engine->objectClass->createPersistentIdentifier(str); - } - break; - - case Instr::FindGenericTerminal: - case Instr::FindGeneric: - // We start the search in the parent context, as we know that the - // name is not present in the current context or it would have been - // found during the static compile - findgeneric(registers + instr->find.reg, config, instr->find.subscribeIndex, - QmlContextPrivate::get(context->parent), - config->identifiers[instr->find.name].identifier, - instr->common.type == Instr::FindGenericTerminal); - break; - - case Instr::FindPropertyTerminal: - case Instr::FindProperty: - { - const Register &object = registers[instr->find.src]; - if (object.isUndefined()) { - throwException(instr->find.exceptionId, error, program, context); - return; - } - - findproperty(object.getQObject(), registers + instr->find.reg, - QmlEnginePrivate::get(context->engine), config, - instr->find.subscribeIndex, config->identifiers[instr->find.name].identifier, - instr->common.type == Instr::FindPropertyTerminal); - } - break; - - case Instr::CleanupGeneric: - { - int type = registers[instr->cleanup.reg].gettype(); - if (type == qMetaTypeId()) { - registers[instr->cleanup.reg].getvariantptr()->~QVariant(); - } else if (type == QMetaType::QString) { - registers[instr->cleanup.reg].getstringptr()->~QString(); - } else if (type == QMetaType::QUrl) { - registers[instr->cleanup.reg].geturlptr()->~QUrl(); - } - } - break; - - case Instr::ConvertGenericToReal: - { - Register &output = registers[instr->unaryop.output]; - Register &input = registers[instr->unaryop.src]; - bool ok = true; - output.setqreal(toReal(&input, input.gettype(), &ok)); - if (!ok) output.setUndefined(); - } - break; - - case Instr::ConvertGenericToBool: - { - Register &output = registers[instr->unaryop.output]; - Register &input = registers[instr->unaryop.src]; - bool ok = true; - output.setbool(toBool(&input, input.gettype(), &ok)); - if (!ok) output.setUndefined(); - } - break; - - case Instr::ConvertGenericToString: - { - Register &output = registers[instr->unaryop.output]; - Register &input = registers[instr->unaryop.src]; - bool ok = true; - QString str = toString(&input, input.gettype(), &ok); - if (ok) { new (output.getstringptr()) QString(str); output.settype(QMetaType::QString); } - else { output.setUndefined(); } - } - break; - - case Instr::ConvertGenericToUrl: - { - Register &output = registers[instr->unaryop.output]; - Register &input = registers[instr->unaryop.src]; - bool ok = true; - QUrl url = toUrl(&input, input.gettype(), context, &ok); - if (ok) { new (output.geturlptr()) QUrl(url); output.settype(QMetaType::QUrl); } - else { output.setUndefined(); } - } - break; - - default: - qFatal("EEK"); - break; - } - - instr++; - } -} - -void QmlBindingVME::dump(const char *programData) -{ - const Program *program = (const Program *)programData; - - qWarning() << "Program.bindings:" << program->bindings; - qWarning() << "Program.dataLength:" << program->dataLength; - qWarning() << "Program.subscriptions:" << program->subscriptions; - qWarning() << "Program.indentifiers:" << program->identifiers; - - int count = program->instructionCount; - const Instr *instr = program->instructions(); - - while (count--) { - - switch (instr->common.type) { - case Instr::Noop: - qWarning().nospace() << "Noop"; - break; - case Instr::Subscribe: - qWarning().nospace() << "Subscribe" << "\t\t" << instr->subscribe.offset << "\t" << instr->subscribe.reg << "\t" << instr->subscribe.index; - break; - case Instr::SubscribeId: - qWarning().nospace() << "SubscribeId" << "\t\t" << instr->subscribe.offset << "\t" << instr->subscribe.reg << "\t" << instr->subscribe.index; - break; - case Instr::LoadId: - qWarning().nospace() << "LoadId" << "\t\t\t" << instr->load.index << "\t" << instr->load.reg; - break; - case Instr::LoadScope: - qWarning().nospace() << "LoadScope" << "\t\t" << instr->load.index << "\t" << instr->load.reg; - break; - case Instr::LoadRoot: - qWarning().nospace() << "LoadRoot" << "\t\t" << instr->load.index << "\t" << instr->load.reg; - break; - case Instr::LoadAttached: - qWarning().nospace() << "LoadAttached" << "\t\t" << instr->attached.output << "\t" << instr->attached.reg << "\t" << instr->attached.index; - break; - case Instr::ConvertIntToReal: - qWarning().nospace() << "ConvertIntToReal" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; - break; - case Instr::ConvertRealToInt: - qWarning().nospace() << "ConvertRealToInt" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; - break; - case Instr::Real: - qWarning().nospace() << "Real" << "\t\t\t" << instr->real_value.reg << "\t" << instr->real_value.value; - break; - case Instr::Int: - qWarning().nospace() << "Int" << "\t\t\t" << instr->int_value.reg << "\t" << instr->int_value.value; - break; - case Instr::Bool: - qWarning().nospace() << "Bool" << "\t\t\t" << instr->bool_value.reg << "\t" << instr->bool_value.value; - break; - case Instr::String: - qWarning().nospace() << "String" << "\t\t\t" << instr->string_value.reg << "\t" << instr->string_value.offset << "\t" << instr->string_value.length; - break; - case Instr::AddReal: - qWarning().nospace() << "AddReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; - break; - case Instr::AddInt: - qWarning().nospace() << "AddInt" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; - break; - case Instr::AddString: - qWarning().nospace() << "AddString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; - break; - case Instr::MinusReal: - qWarning().nospace() << "MinusReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; - break; - case Instr::MinusInt: - qWarning().nospace() << "MinusInt" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; - break; - case Instr::CompareReal: - qWarning().nospace() << "CompareReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; - break; - case Instr::CompareString: - qWarning().nospace() << "CompareString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; - break; - case Instr::NotCompareReal: - qWarning().nospace() << "NotCompareReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; - break; - case Instr::NotCompareString: - qWarning().nospace() << "NotCompareString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; - break; - case Instr::GreaterThanReal: - qWarning().nospace() << "GreaterThanReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; - break; - case Instr::MaxReal: - qWarning().nospace() << "MaxReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; - break; - case Instr::MinReal: - qWarning().nospace() << "MinReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; - break; - case Instr::NewString: - qWarning().nospace() << "NewString" << "\t\t" << instr->construct.reg; - break; - case Instr::NewUrl: - qWarning().nospace() << "NewUrl" << "\t\t\t" << instr->construct.reg; - break; - case Instr::CleanupString: - qWarning().nospace() << "CleanupString" << "\t\t" << instr->cleanup.reg; - break; - case Instr::CleanupUrl: - qWarning().nospace() << "CleanupUrl" << "\t\t" << instr->cleanup.reg; - break; - case Instr::Fetch: - qWarning().nospace() << "Fetch" << "\t\t\t" << instr->fetch.output << "\t" << instr->fetch.index << "\t" << instr->fetch.objectReg; - break; - case Instr::Store: - qWarning().nospace() << "Store" << "\t\t\t" << instr->store.output << "\t" << instr->store.index << "\t" << instr->store.reg; - break; - case Instr::Copy: - qWarning().nospace() << "Copy" << "\t\t\t" << instr->copy.reg << "\t" << instr->copy.src; - break; - case Instr::Skip: - qWarning().nospace() << "Skip" << "\t\t\t" << instr->skip.reg << "\t" << instr->skip.count; - break; - case Instr::Done: - qWarning().nospace() << "Done"; - break; - case Instr::InitString: - qWarning().nospace() << "InitString" << "\t\t" << instr->initstring.offset << "\t" << instr->initstring.dataIdx; - break; - case Instr::FindGeneric: - qWarning().nospace() << "FindGeneric" << "\t\t" << instr->find.reg << "\t" << instr->find.name; - break; - case Instr::FindGenericTerminal: - qWarning().nospace() << "FindGenericTerminal" << "\t" << instr->find.reg << "\t" << instr->find.name; - break; - case Instr::FindProperty: - qWarning().nospace() << "FindProperty" << "\t\t" << instr->find.reg << "\t" << instr->find.src << "\t" << instr->find.name; - break; - case Instr::FindPropertyTerminal: - qWarning().nospace() << "FindPropertyTerminal" << "\t" << instr->find.reg << "\t" << instr->find.src << "\t" << instr->find.name; - break; - case Instr::CleanupGeneric: - qWarning().nospace() << "CleanupGeneric" << "\t\t" << instr->cleanup.reg; - break; - case Instr::ConvertGenericToReal: - qWarning().nospace() << "ConvertGenericToReal" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; - break; - case Instr::ConvertGenericToBool: - qWarning().nospace() << "ConvertGenericToBool" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; - break; - case Instr::ConvertGenericToString: - qWarning().nospace() << "ConvertGenericToString" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; - break; - case Instr::ConvertGenericToUrl: - qWarning().nospace() << "ConvertGenericToUrl" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; - break; - default: - qWarning().nospace() << "Unknown"; - break; - } - - ++instr; - } -} - -/*! -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; - registerCleanups.clear(); - data = committed.data; - exceptions = committed.exceptions; - usedSubscriptionIds.clear(); - subscriptionSet.clear(); - subscriptionIds = committed.subscriptionIds; - registeredStrings = committed.registeredStrings; - bytecode.clear(); -} - -/*! -Mark the last compile as successful, and add it to the "committed data" -section. - -Returns the index for the committed binding. -*/ -int QmlBindingCompilerPrivate::commitCompile() -{ - int rv = committed.count(); - committed.offsets << committed.bytecode.count(); - committed.dependencies << usedSubscriptionIds; - committed.bytecode << bytecode; - committed.data = data; - committed.exceptions = exceptions; - committed.subscriptionIds = subscriptionIds; - committed.registeredStrings = registeredStrings; - return rv; -} - -bool QmlBindingCompilerPrivate::compile(QmlJS::AST::Node *node) -{ - resetInstanceState(); - - Result type; - - if (!parseExpression(node, type)) - return false; - - if (subscriptionSet.count() > 0xFFFF || - registeredStrings.count() > 0xFFFF) - return false; - - if (type.unknownType) { - if (destination->type != QMetaType::QReal && - destination->type != QVariant::String && - destination->type != QMetaType::Bool && - destination->type != QVariant::Url) - return false; - - int convertReg = acquireReg(); - - if (destination->type == QMetaType::QReal) { - Instr convert; - convert.common.type = Instr::ConvertGenericToReal; - convert.unaryop.output = convertReg; - convert.unaryop.src = type.reg; - bytecode << convert; - } else if (destination->type == QVariant::String) { - Instr convert; - convert.common.type = Instr::ConvertGenericToString; - convert.unaryop.output = convertReg; - convert.unaryop.src = type.reg; - bytecode << convert; - } else if (destination->type == QMetaType::Bool) { - Instr convert; - convert.common.type = Instr::ConvertGenericToBool; - convert.unaryop.output = convertReg; - convert.unaryop.src = type.reg; - bytecode << convert; - } else if (destination->type == QVariant::Url) { - Instr convert; - convert.common.type = Instr::ConvertGenericToUrl; - convert.unaryop.output = convertReg; - convert.unaryop.src = type.reg; - bytecode << convert; - } - - Instr cleanup; - cleanup.common.type = Instr::CleanupGeneric; - cleanup.cleanup.reg = type.reg; - bytecode << cleanup; - - Instr instr; - instr.common.type = Instr::Store; - instr.store.output = 0; - instr.store.index = destination->index; - instr.store.reg = convertReg; - instr.store.exceptionId = exceptionId(node->expressionCast()); - bytecode << instr; - - if (destination->type == QVariant::String) { - Instr cleanup; - cleanup.common.type = Instr::CleanupString; - cleanup.cleanup.reg = convertReg; - bytecode << cleanup; - } else if (destination->type == QVariant::Url) { - Instr cleanup; - cleanup.common.type = Instr::CleanupUrl; - cleanup.cleanup.reg = convertReg; - bytecode << cleanup; - } - - releaseReg(convertReg); - - Instr done; - done.common.type = Instr::Done; - bytecode << done; - - return true; - } else { - // Can we store the final value? - if (type.type == QVariant::Int && - destination->type == QMetaType::QReal) { - Instr instr; - instr.common.type = Instr::ConvertIntToReal; - instr.unaryop.output = type.reg; - instr.unaryop.src = type.reg; - bytecode << instr; - type.type = QMetaType::QReal; - } else if (type.type == QMetaType::QReal && - destination->type == QVariant::Int) { - Instr instr; - instr.common.type = Instr::ConvertRealToInt; - instr.unaryop.output = type.reg; - instr.unaryop.src = type.reg; - bytecode << instr; - type.type = QVariant::Int; - } else if (type.type == destination->type) { - } else { - const QMetaObject *from = type.metaObject; - const QMetaObject *to = QmlMetaType::rawMetaObjectForType(destination->type); - - if (QmlMetaPropertyPrivate::canConvert(from, to)) - type.type = destination->type; - } - - if (type.type == destination->type) { - Instr instr; - instr.common.type = Instr::Store; - instr.store.output = 0; - instr.store.index = destination->index; - instr.store.reg = type.reg; - instr.store.exceptionId = exceptionId(node->expressionCast()); - bytecode << instr; - - releaseReg(type.reg); - - Instr done; - done.common.type = Instr::Done; - bytecode << done; - - return true; - } else { - return false; - } - } -} - -bool QmlBindingCompilerPrivate::parseExpression(QmlJS::AST::Node *node, Result &type) -{ - while (node->kind == AST::Node::Kind_NestedExpression) - node = static_cast(node)->expression; - - if (tryArith(node)) { - if (!parseArith(node, type)) return false; - } else if (tryLogic(node)) { - if (!parseLogic(node, type)) return false; - } else if (tryConditional(node)) { - if (!parseConditional(node, type)) return false; - } else if (tryName(node)) { - if (!parseName(node, type)) return false; - } else if (tryConstant(node)) { - if (!parseConstant(node, type)) return false; - } else if (tryMethod(node)) { - if (!parseMethod(node, type)) return false; - } else { - return false; - } - return true; -} - -bool QmlBindingCompilerPrivate::tryName(QmlJS::AST::Node *node) -{ - return node->kind == AST::Node::Kind_IdentifierExpression || - node->kind == AST::Node::Kind_FieldMemberExpression; -} - -bool QmlBindingCompilerPrivate::parseName(AST::Node *node, Result &type) -{ - QStringList nameParts; - QList nameNodes; - if (!buildName(nameParts, node, &nameNodes)) - return false; - - int reg = acquireReg(); - if (reg == -1) - return false; - type.reg = reg; - - QmlParser::Object *absType = 0; - - QStringList subscribeName; - - bool wasAttachedObject = false; - - for (int ii = 0; ii < nameParts.count(); ++ii) { - const QString &name = nameParts.at(ii); - - // We don't handle signal properties or attached properties - if (name.length() > 2 && name.startsWith(QLatin1String("on")) && - name.at(2).isUpper()) - return false; - - QmlType *attachType = 0; - if (name.at(0).isUpper()) { - // Could be an attached property - if (ii == nameParts.count() - 1) - return false; - if (nameParts.at(ii + 1).at(0).isUpper()) - return false; - - QmlEnginePrivate::ImportedNamespace *ns = 0; - if (!engine->resolveType(imports, name.toUtf8(), &attachType, 0, 0, 0, &ns)) - return false; - if (ns || !attachType || !attachType->attachedPropertiesType()) - return false; - - wasAttachedObject = true; - } - - if (ii == 0) { - - if (attachType) { - Instr instr; - instr.common.type = Instr::LoadScope; - instr.load.index = 0; - instr.load.reg = reg; - bytecode << instr; - - Instr attach; - attach.common.type = Instr::LoadAttached; - attach.attached.output = reg; - attach.attached.reg = reg; - attach.attached.index = attachType->index(); - attach.attached.exceptionId = exceptionId(nameNodes.at(ii)); - bytecode << attach; - - subscribeName << contextName(); - subscribeName << QLatin1String("$$$ATTACH_") + name; - - absType = 0; - type.metaObject = attachType->attachedPropertiesType(); - - continue; - } else if (ids.contains(name)) { - QmlParser::Object *idObject = ids.value(name); - absType = idObject; - type.metaObject = absType->metaObject(); - - // We check if the id object is the root or - // scope object to avoid a subscription - if (idObject == component) { - Instr instr; - instr.common.type = Instr::LoadRoot; - instr.load.index = 0; - instr.load.reg = reg; - bytecode << instr; - } else if (idObject == context) { - Instr instr; - instr.common.type = Instr::LoadScope; - instr.load.index = 0; - instr.load.reg = reg; - bytecode << instr; - } else { - Instr instr; - instr.common.type = Instr::LoadId; - instr.load.index = idObject->idIndex; - instr.load.reg = reg; - bytecode << instr; - - subscribeName << QLatin1String("$$$ID_") + name; - - if (subscription(subscribeName, &type)) { - Instr sub; - sub.common.type = Instr::SubscribeId; - sub.subscribe.offset = subscriptionIndex(subscribeName); - sub.subscribe.reg = reg; - sub.subscribe.index = instr.load.index; - bytecode << sub; - } - } - - } else { - - QByteArray utf8Name = name.toUtf8(); - const char *cname = utf8Name.constData(); - - int d0Idx = (context == component)?-1:context->metaObject()->indexOfProperty(cname); - int d1Idx = -1; - if (d0Idx == -1) - d1Idx = component->metaObject()->indexOfProperty(cname); - - if (d0Idx != -1) { - Instr instr; - instr.common.type = Instr::LoadScope; - instr.load.index = 0; - instr.load.reg = reg; - bytecode << instr; - - subscribeName << contextName(); - subscribeName << name; - - fetch(type, context->metaObject(), reg, d0Idx, subscribeName, nameNodes.at(ii)); - } else if(d1Idx != -1) { - Instr instr; - instr.common.type = Instr::LoadRoot; - instr.load.index = 0; - instr.load.reg = reg; - bytecode << instr; - - subscribeName << QLatin1String("$$$ROOT"); - subscribeName << name; - - fetch(type, component->metaObject(), reg, d1Idx, subscribeName, nameNodes.at(ii)); - } else { - Instr find; - if (nameParts.count() == 1) - find.common.type = Instr::FindGenericTerminal; - else - find.common.type = Instr::FindGeneric; - - find.find.reg = reg; - find.find.src = -1; - find.find.name = registerString(name); - find.find.exceptionId = exceptionId(nameNodes.at(ii)); - - subscribeName << QString(QLatin1String("$$$Generic_") + name); - if (subscription(subscribeName, &type)) - find.find.subscribeIndex = subscriptionIndex(subscribeName); - else - find.find.subscribeIndex = -1; - - bytecode << find; - type.unknownType = true; - } - - if (!type.unknownType && type.type == -1) - return false; // Couldn't fetch that type - } - - } else { - - if (attachType) { - Instr attach; - attach.common.type = Instr::LoadAttached; - attach.attached.output = reg; - attach.attached.reg = reg; - attach.attached.index = attachType->index(); - bytecode << attach; - - absType = 0; - type.metaObject = attachType->attachedPropertiesType(); - - subscribeName << QLatin1String("$$$ATTACH_") + name; - continue; - } - - const QMetaObject *mo = 0; - if (absType) - mo = absType->metaObject(); - else if (type.metaObject) - mo = type.metaObject; - - QByteArray utf8Name = name.toUtf8(); - const char *cname = utf8Name.constData(); - int idx = mo?mo->indexOfProperty(cname):-1; - if (absType && idx == -1) - return false; - - subscribeName << name; - - if (absType || (wasAttachedObject && idx != -1) || (mo && mo->property(idx).isFinal())) { - absType = 0; - fetch(type, mo, reg, idx, subscribeName, nameNodes.at(ii)); - if (type.type == -1) - return false; - } else { - - Instr prop; - if (ii == nameParts.count() -1 ) - prop.common.type = Instr::FindPropertyTerminal; - else - prop.common.type = Instr::FindProperty; - - prop.find.reg = reg; - prop.find.src = reg; - prop.find.name = registerString(name); - prop.find.exceptionId = exceptionId(nameNodes.at(ii)); - - if (subscription(subscribeName, &type)) - prop.find.subscribeIndex = subscriptionIndex(subscribeName); - else - prop.find.subscribeIndex = -1; - - type.unknownType = true; - type.metaObject = 0; - type.type = -1; - type.reg = reg; - bytecode << prop; - } - } - - wasAttachedObject = false; - } - - return true; -} - -bool QmlBindingCompilerPrivate::tryArith(QmlJS::AST::Node *node) -{ - if (node->kind != AST::Node::Kind_BinaryExpression) - return false; - - AST::BinaryExpression *expression = static_cast(node); - if (expression->op == QSOperator::Add || - expression->op == QSOperator::Sub) - return true; - else - return false; -} - -bool QmlBindingCompilerPrivate::parseArith(QmlJS::AST::Node *node, Result &type) -{ - AST::BinaryExpression *expression = static_cast(node); - - type.reg = acquireReg(); - - Result lhs; - Result rhs; - - if (!parseExpression(expression->left, lhs)) return false; - if (!parseExpression(expression->right, rhs)) return false; - - if ((lhs.type == QVariant::Int || lhs.type == QMetaType::QReal) && - (rhs.type == QVariant::Int || rhs.type == QMetaType::QReal)) - return numberArith(type, lhs, rhs, (QSOperator::Op)expression->op); - else if(expression->op == QSOperator::Sub) - return numberArith(type, lhs, rhs, (QSOperator::Op)expression->op); - else if ((lhs.type == QMetaType::QString || lhs.unknownType) && - (rhs.type == QMetaType::QString || rhs.unknownType) && - (lhs.type == QMetaType::QString || rhs.type == QMetaType::QString)) - return stringArith(type, lhs, rhs, (QSOperator::Op)expression->op); - else - return false; -} - -bool QmlBindingCompilerPrivate::numberArith(Result &type, const Result &lhs, const Result &rhs, QSOperator::Op op) -{ - bool nativeReal = rhs.type == QMetaType::QReal || - lhs.type == QMetaType::QReal || - lhs.unknownType || - rhs.unknownType; - - if (nativeReal && lhs.type == QMetaType::Int) { - Instr convert; - convert.common.type = Instr::ConvertIntToReal; - convert.unaryop.output = lhs.reg; - convert.unaryop.src = lhs.reg; - bytecode << convert; - } - - if (nativeReal && rhs.type == QMetaType::Int) { - Instr convert; - convert.common.type = Instr::ConvertIntToReal; - convert.unaryop.output = rhs.reg; - convert.unaryop.src = rhs.reg; - bytecode << convert; - } - - int lhsTmp = -1; - int rhsTmp = -1; - if (lhs.unknownType) { - lhsTmp = acquireReg(); - - Instr conv; - conv.common.type = Instr::ConvertGenericToReal; - conv.unaryop.output = lhsTmp; - conv.unaryop.src = lhs.reg; - bytecode << conv; - } - - if (rhs.unknownType) { - rhsTmp = acquireReg(); - - Instr conv; - conv.common.type = Instr::ConvertGenericToReal; - conv.unaryop.output = rhsTmp; - conv.unaryop.src = rhs.reg; - bytecode << conv; - } - - Instr arith; - if (op == QSOperator::Add) { - arith.common.type = nativeReal?Instr::AddReal:Instr::AddInt; - } else if (op == QSOperator::Sub) { - arith.common.type = nativeReal?Instr::MinusReal:Instr::MinusInt; - } else { - qFatal("Unsupported arithmetic operator"); - } - - arith.binaryop.output = type.reg; - arith.binaryop.src1 = (lhsTmp == -1)?lhs.reg:lhsTmp; - arith.binaryop.src2 = (rhsTmp == -1)?rhs.reg:rhsTmp; - bytecode << arith; - - type.metaObject = 0; - type.type = nativeReal?QMetaType::QReal:QMetaType::Int; - type.subscriptionSet.unite(lhs.subscriptionSet); - type.subscriptionSet.unite(rhs.subscriptionSet); - - if (lhsTmp != -1) releaseReg(lhsTmp); - if (rhsTmp != -1) releaseReg(rhsTmp); - releaseReg(lhs.reg); - releaseReg(rhs.reg); - - return true; -} - -bool QmlBindingCompilerPrivate::stringArith(Result &type, const Result &lhs, const Result &rhs, QSOperator::Op op) -{ - if (op != QSOperator::Add) - return false; - - int lhsTmp = -1; - int rhsTmp = -1; - - if (lhs.unknownType) { - lhsTmp = acquireReg(Instr::CleanupString); - - Instr convert; - convert.common.type = Instr::ConvertGenericToString; - convert.unaryop.output = lhsTmp; - convert.unaryop.src = lhs.reg; - bytecode << convert; - } - - if (rhs.unknownType) { - rhsTmp = acquireReg(Instr::CleanupString); - - Instr convert; - convert.common.type = Instr::ConvertGenericToString; - convert.unaryop.output = rhsTmp; - convert.unaryop.src = rhs.reg; - bytecode << convert; - } - - type.reg = acquireReg(Instr::CleanupString); - type.type = QMetaType::QString; - - Instr add; - add.common.type = Instr::AddString; - add.binaryop.output = type.reg; - add.binaryop.src1 = (lhsTmp == -1)?lhs.reg:lhsTmp; - add.binaryop.src2 = (rhsTmp == -1)?rhs.reg:rhsTmp; - bytecode << add; - - if (lhsTmp != -1) releaseReg(lhsTmp); - if (rhsTmp != -1) releaseReg(rhsTmp); - - return true; -} - -bool QmlBindingCompilerPrivate::tryLogic(QmlJS::AST::Node *node) -{ - if (node->kind != AST::Node::Kind_BinaryExpression) - return false; - - AST::BinaryExpression *expression = static_cast(node); - if (expression->op == QSOperator::Gt || - expression->op == QSOperator::Equal || - expression->op == QSOperator::NotEqual) - return true; - else - return false; -} - -bool QmlBindingCompilerPrivate::parseLogic(QmlJS::AST::Node *node, Result &type) -{ - AST::BinaryExpression *expression = static_cast(node); - - Result lhs; - Result rhs; - - if (!parseExpression(expression->left, lhs)) return false; - if (!parseExpression(expression->right, rhs)) return false; - - type.reg = acquireReg(); - type.metaObject = 0; - type.type = QVariant::Bool; - - if (lhs.type == QMetaType::QReal && rhs.type == QMetaType::QReal) { - - Instr op; - if (expression->op == QSOperator::Gt) - op.common.type = Instr::GreaterThanReal; - else if (expression->op == QSOperator::Equal) - op.common.type = Instr::CompareReal; - else if (expression->op == QSOperator::NotEqual) - op.common.type = Instr::NotCompareReal; - else - return false; - op.binaryop.output = type.reg; - op.binaryop.src1 = lhs.reg; - op.binaryop.src2 = rhs.reg; - bytecode << op; - - - } else if (lhs.type == QMetaType::QString && rhs.type == QMetaType::QString) { - - Instr op; - if (expression->op == QSOperator::Equal) - op.common.type = Instr::CompareString; - else if (expression->op == QSOperator::NotEqual) - op.common.type = Instr::NotCompareString; - else - return false; - op.binaryop.output = type.reg; - op.binaryop.src1 = lhs.reg; - op.binaryop.src2 = rhs.reg; - bytecode << op; - - } else { - return false; - } - - releaseReg(lhs.reg); - releaseReg(rhs.reg); - - return true; -} - -bool QmlBindingCompilerPrivate::tryConditional(QmlJS::AST::Node *node) -{ - return (node->kind == AST::Node::Kind_ConditionalExpression); -} - -bool QmlBindingCompilerPrivate::parseConditional(QmlJS::AST::Node *node, Result &type) -{ - AST::ConditionalExpression *expression = static_cast(node); - - AST::Node *test = expression->expression; - if (test->kind == AST::Node::Kind_NestedExpression) - test = static_cast(test)->expression; - - Result etype; - if (!parseExpression(test, etype)) return false; - - if (etype.type != QVariant::Bool) - return false; - - Instr skip; - skip.common.type = Instr::Skip; - skip.skip.reg = etype.reg; - skip.skip.count = 0; - int skipIdx = bytecode.count(); - bytecode << skip; - - // Release to allow reuse of reg - releaseReg(etype.reg); - - QSet preSubSet = subscriptionSet; - - // int preConditionalSubscriptions = subscriptionSet.count(); - - Result ok; - if (!parseExpression(expression->ok, ok)) return false; - if (ok.unknownType) return false; - - int skipIdx2 = bytecode.count(); - skip.skip.reg = -1; - bytecode << skip; - - // Release to allow reuse of reg - releaseReg(ok.reg); - bytecode[skipIdx].skip.count = bytecode.count() - skipIdx - 1; - - subscriptionSet = preSubSet; - - Result ko; - if (!parseExpression(expression->ko, ko)) return false; - if (ko.unknownType) return false; - - // Release to allow reuse of reg - releaseReg(ko.reg); - bytecode[skipIdx2].skip.count = bytecode.count() - skipIdx2 - 1; - - if (ok != ko) - return false; // Must be same type and in same register - - subscriptionSet = preSubSet; - - if (!subscriptionNeutral(subscriptionSet, ok.subscriptionSet, ko.subscriptionSet)) - return false; // Conditionals cannot introduce new subscriptions - - type = ok; - - return true; -} - -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 || - node->kind == AST::Node::Kind_StringLiteral; -} - -bool QmlBindingCompilerPrivate::parseConstant(QmlJS::AST::Node *node, Result &type) -{ - type.metaObject = 0; - type.type = -1; - type.reg = acquireReg(); - - if (node->kind == AST::Node::Kind_TrueLiteral) { - type.type = QVariant::Bool; - Instr instr; - instr.common.type = Instr::Bool; - instr.bool_value.reg = type.reg; - instr.bool_value.value = true; - bytecode << instr; - return true; - } else if (node->kind == AST::Node::Kind_FalseLiteral) { - type.type = QVariant::Bool; - Instr instr; - instr.common.type = Instr::Bool; - instr.bool_value.reg = type.reg; - instr.bool_value.value = false; - bytecode << instr; - return true; - } else if (node->kind == AST::Node::Kind_NumericLiteral) { - qreal value = qreal(static_cast(node)->value); - - if (qreal(float(value)) != value) - return false; - - type.type = QMetaType::QReal; - Instr instr; - instr.common.type = Instr::Real; - instr.real_value.reg = type.reg; - instr.real_value.value = float(value); - bytecode << instr; - return true; - } else if (node->kind == AST::Node::Kind_StringLiteral) { - QString str = static_cast(node)->value->asString(); - type.type = QMetaType::QString; - type.reg = registerLiteralString(str); - return true; - } else { - return false; - } -} - -bool QmlBindingCompilerPrivate::tryMethod(QmlJS::AST::Node *node) -{ - return node->kind == AST::Node::Kind_CallExpression; -} - -bool QmlBindingCompilerPrivate::parseMethod(QmlJS::AST::Node *node, Result &result) -{ - AST::CallExpression *expr = static_cast(node); - - QStringList name; - if (!buildName(name, expr->base)) - return false; - - if (name.count() != 2 || name.at(0) != QLatin1String("Math")) - return false; - - QString method = name.at(1); - - AST::ArgumentList *args = expr->arguments; - if (!args) return false; - AST::ExpressionNode *arg0 = args->expression; - args = args->next; - if (!args) return false; - AST::ExpressionNode *arg1 = args->expression; - if (args->next != 0) return false; - if (!arg0 || !arg1) return false; - - Result r0; - if (!parseExpression(arg0, r0)) return false; - Result r1; - if (!parseExpression(arg1, r1)) return false; - - if (r0.type != QMetaType::QReal || r1.type != QMetaType::QReal) - return false; - - Instr op; - if (method == QLatin1String("max")) { - op.common.type = Instr::MaxReal; - } else if (method == QLatin1String("min")) { - op.common.type = Instr::MinReal; - } else { - return false; - } - // We release early to reuse registers - releaseReg(r0.reg); - releaseReg(r1.reg); - - op.binaryop.output = acquireReg(); - op.binaryop.src1 = r0.reg; - op.binaryop.src2 = r1.reg; - bytecode << op; - - result.type = QMetaType::QReal; - result.reg = op.binaryop.output; - - return true; -} - -bool QmlBindingCompilerPrivate::buildName(QStringList &name, - QmlJS::AST::Node *node, - QList *nodes) -{ - if (node->kind == AST::Node::Kind_IdentifierExpression) { - name << static_cast(node)->name->asString(); - if (nodes) *nodes << static_cast(node); - } else if (node->kind == AST::Node::Kind_FieldMemberExpression) { - AST::FieldMemberExpression *expr = - static_cast(node); - - if (!buildName(name, expr->base, nodes)) - return false; - - name << expr->name->asString(); - if (nodes) *nodes << expr; - } else { - return false; - } - - return true; -} - - -bool QmlBindingCompilerPrivate::fetch(Result &rv, const QMetaObject *mo, int reg, - int idx, const QStringList &subName, QmlJS::AST::ExpressionNode *node) -{ - QMetaProperty prop = mo->property(idx); - rv.metaObject = 0; - rv.type = 0; - - if (subscription(subName, &rv) && prop.hasNotifySignal() && prop.notifySignalIndex() != -1) { - Instr sub; - sub.common.type = Instr::Subscribe; - sub.subscribe.offset = subscriptionIndex(subName); - sub.subscribe.reg = reg; - sub.subscribe.index = prop.notifySignalIndex(); - bytecode << sub; - } - - Instr fetch; - fetch.common.type = Instr::Fetch; - fetch.fetch.objectReg = reg; - fetch.fetch.index = idx; - fetch.fetch.output = reg; - fetch.fetch.exceptionId = exceptionId(node); - - rv.type = prop.userType(); - rv.metaObject = QmlMetaType::metaObjectForType(rv.type); - rv.reg = reg; - - if (rv.type == QMetaType::QString) { - int tmp = acquireReg(); - Instr copy; - copy.common.type = Instr::Copy; - copy.copy.reg = tmp; - copy.copy.src = reg; - bytecode << copy; - releaseReg(tmp); - fetch.fetch.objectReg = tmp; - - Instr setup; - setup.common.type = Instr::NewString; - setup.construct.reg = reg; - bytecode << setup; - registerCleanup(reg, Instr::CleanupString); - } - - bytecode << fetch; - - if (!rv.metaObject && - rv.type != QMetaType::QReal && - rv.type != QMetaType::Int && - rv.type != QMetaType::Bool && - rv.type != qMetaTypeId() && - rv.type != QMetaType::QString) { - rv.metaObject = 0; - rv.type = 0; - return false; // Unsupported type (string not supported yet); - } - - return true; -} - -void QmlBindingCompilerPrivate::registerCleanup(int reg, int cleanup, int cleanupType) -{ - registerCleanups.insert(reg, qMakePair(cleanup, cleanupType)); -} - -int QmlBindingCompilerPrivate::acquireReg(int cleanup, int cleanupType) -{ - for (int ii = 0; ii < 32; ++ii) { - if (!(registers & (1 << ii))) { - registers |= (1 << ii); - - if (cleanup != Instr::Noop) - registerCleanup(ii, cleanup, cleanupType); - - return ii; - } - } - return -1; -} - -void QmlBindingCompilerPrivate::releaseReg(int reg) -{ - Q_ASSERT(reg >= 0 && reg <= 31); - - if (registerCleanups.contains(reg)) { - QPair c = registerCleanups[reg]; - registerCleanups.remove(reg); - Instr cleanup; - cleanup.common.type = (quint8)c.first; - cleanup.cleanup.reg = reg; - bytecode << cleanup; - } - - quint32 mask = 1 << reg; - registers &= ~mask; -} - -// Returns a reg -int QmlBindingCompilerPrivate::registerLiteralString(const QString &str) -{ - QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar)); - int offset = data.count(); - data += strdata; - - int reg = acquireReg(Instr::CleanupString); - - Instr string; - string.common.type = Instr::String; - string.string_value.reg = reg; - string.string_value.offset = offset; - string.string_value.length = str.length(); - bytecode << string; - - return reg; -} - -// Returns an identifier offset -int QmlBindingCompilerPrivate::registerString(const QString &string) -{ - Q_ASSERT(!string.isEmpty()); - - QHash >::ConstIterator iter = registeredStrings.find(string); - - if (iter == registeredStrings.end()) { - quint32 len = string.length(); - QByteArray lendata((const char *)&len, sizeof(quint32)); - QByteArray strdata((const char *)string.constData(), string.length() * sizeof(QChar)); - strdata.prepend(lendata); - int rv = data.count(); - data += strdata; - - iter = registeredStrings.insert(string, qMakePair(registeredStrings.count(), rv)); - } - - Instr reg; - reg.common.type = Instr::InitString; - reg.initstring.offset = iter->first; - reg.initstring.dataIdx = iter->second; - bytecode << reg; - return reg.initstring.offset; -} - -bool QmlBindingCompilerPrivate::subscription(const QStringList &sub, Result *result) -{ - QString str = sub.join(QLatin1String(".")); - result->subscriptionSet.insert(str); - - if (subscriptionSet.contains(str)) { - return false; - } else { - subscriptionSet.insert(str); - return true; - } -} - -int QmlBindingCompilerPrivate::subscriptionIndex(const QStringList &sub) -{ - QString str = sub.join(QLatin1String(".")); - QHash::ConstIterator iter = subscriptionIds.find(str); - if (iter == subscriptionIds.end()) - iter = subscriptionIds.insert(str, subscriptionIds.count()); - usedSubscriptionIds.insert(*iter); - return *iter; -} - -/* - 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 QmlBindingCompilerPrivate::subscriptionNeutral(const QSet &base, - const QSet &lhs, - const QSet &rhs) -{ - QSet difflhs = lhs; - difflhs.subtract(rhs); - QSet diffrhs = rhs; - diffrhs.subtract(lhs); - - difflhs.unite(diffrhs); - difflhs.subtract(base); - - return difflhs.isEmpty(); -} - -quint8 QmlBindingCompilerPrivate::exceptionId(QmlJS::AST::ExpressionNode *n) -{ - quint8 rv = 0xFF; - if (n && exceptions.count() < 0xFF) { - rv = (quint8)exceptions.count(); - QmlJS::AST::SourceLocation l = n->firstSourceLocation(); - quint64 e = l.startLine; - e <<= 32; - e |= l.startColumn; - exceptions.append(e); - } - return rv; -} - -QmlBindingCompiler::QmlBindingCompiler() -: d(new QmlBindingCompilerPrivate) -{ -} - -QmlBindingCompiler::~QmlBindingCompiler() -{ - delete d; d = 0; -} - -/* -Returns true if any bindings were compiled. -*/ -bool QmlBindingCompiler::isValid() const -{ - return !d->committed.bytecode.isEmpty(); -} - -/* --1 on failure, otherwise the binding index to use. -*/ -int QmlBindingCompiler::compile(const 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 > table; - - for (int ii = 0; ii < committed.count(); ++ii) { - const QSet &deps = committed.dependencies.at(ii); - for (QSet::ConstIterator iter = deps.begin(); iter != deps.end(); ++iter) - table[*iter].append(ii); - } - - QVector header; - QVector data; - for (int ii = 0; ii < committed.subscriptionIds.count(); ++ii) { - header.append(committed.subscriptionIds.count() + data.count()); - const QList &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)); -} - -QByteArray QmlBindingCompilerPrivate::buildExceptionData() const -{ - QByteArray rv; - rv.resize(committed.exceptions.count() * sizeof(quint64)); - ::memcpy(rv.data(), committed.exceptions.constData(), rv.size()); - return rv; -} - -/* -Returns the compiled program. -*/ -QByteArray QmlBindingCompiler::program() const -{ - QByteArray programData; - - if (isValid()) { - Program prog; - prog.bindings = d->committed.count(); - - QVector bytecode; - Instr skip; - skip.common.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(); - while (data.count() % 4) data.append('\0'); - prog.exceptionDataOffset = data.count(); - data += d->buildExceptionData(); - - prog.dataLength = 4 * ((data.size() + 3) / 4); - prog.subscriptions = d->committed.subscriptionIds.count(); - prog.identifiers = d->committed.registeredStrings.count(); - prog.instructionCount = bytecode.count(); - 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 deleted file mode 100644 index 01280f0..0000000 --- a/src/declarative/qml/qmlbindingvme_p.h +++ /dev/null @@ -1,182 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the Technology Preview License Agreement accompanying -** this package. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** If you have questions regarding the use of this file, please contact -** Nokia at qt-info@nokia.com. -** -** -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QMLBINDINGVME_P_H -#define QMLBINDINGVME_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 -#include -#include "qmlexpression_p.h" -#include "qmlguard_p.h" - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -class QObject; -class QmlContextPrivate; -class QmlBindingVME -{ -public: - struct Config { - Config() : target(0), targetSlot(-1), subscriptions(0), identifiers(0) {} - ~Config() { delete [] subscriptions; delete [] identifiers; } - QObject *target; - int targetSlot; - - struct Subscription { - struct Signal { - QmlGuard source; - int notifyIndex; - }; - - struct Id { - inline Id(); - inline ~Id(); - inline void reset(); - Id *next; - Id**prev; - QObject *target; - int methodIndex; - }; - - enum { InvalidType, SignalType, IdType } type; - inline Subscription(); - inline ~Subscription(); - bool isSignal() const { return type == SignalType; } - bool isId() const { return type == IdType; } - inline Signal *signal(); - inline Id *id(); - union { - char signalData[sizeof(Signal)]; - char idData[sizeof(Id)]; - }; - }; - Subscription *subscriptions; - QScriptDeclarativeClass::PersistentIdentifier *identifiers; - }; - - static void init(const char *program, Config *config, - quint32 **sigTable, quint32 *bindingCount); - static void run(const char *program, int instr, - Config *config, QmlContextPrivate *context, QmlDelayedError *error, - QObject *scope, QObject *output); - static void dump(const char *); -}; - -QmlBindingVME::Config::Subscription::Subscription() -: type(InvalidType) -{ -} - -QmlBindingVME::Config::Subscription::~Subscription() -{ - if (type == SignalType) ((Signal *)signalData)->~Signal(); - else if (type == IdType) ((Id *)idData)->~Id(); -} - -QmlBindingVME::Config::Subscription::Id::Id() -: next(0), prev(0), target(0), methodIndex(-1) -{ -} - -QmlBindingVME::Config::Subscription::Id::~Id() -{ - reset(); -} - -void QmlBindingVME::Config::Subscription::Id::reset() -{ - if (next) next->prev = prev; - if (prev) *prev = next; - next = 0; - prev = 0; - target = 0; - methodIndex = -1; -} - -class QmlBindingCompilerPrivate; -class QmlBindingCompiler -{ -public: - QmlBindingCompiler(); - ~QmlBindingCompiler(); - - // Returns true if bindings were compiled - bool isValid() const; - - struct Expression - { - QmlParser::Object *component; - QmlParser::Object *context; - QmlParser::Property *property; - QmlParser::Variant expression; - QHash ids; - QmlEnginePrivate::Imports imports; - }; - - // -1 on failure, otherwise the binding index to use - int compile(const Expression &, QmlEnginePrivate *); - - // Returns the compiled program - QByteArray program() const; - -private: - QmlBindingCompilerPrivate *d; -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // QMLBINDINGVME_P_H - diff --git a/src/declarative/qml/qmlcompiledbindings.cpp b/src/declarative/qml/qmlcompiledbindings.cpp new file mode 100644 index 0000000..d09f7eb --- /dev/null +++ b/src/declarative/qml/qmlcompiledbindings.cpp @@ -0,0 +1,2749 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmlcompiledbindings_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +using namespace QmlJS; + +namespace { +// Supported types: int, qreal, QString (needs constr/destr), QObject*, bool +struct Register { + void setUndefined() { type = 0; } + void setUnknownButDefined() { type = -1; } + void setNaN() { setqreal(qSNaN()); } + bool isUndefined() const { return type == 0; } + + void setQObject(QObject *o) { *((QObject **)data) = o; type = QMetaType::QObjectStar; } + QObject *getQObject() const { return *((QObject **)data); } + + void setqreal(qreal v) { *((qreal *)data) = v; type = QMetaType::QReal; } + qreal getqreal() const { return *((qreal *)data); } + + void setint(int v) { *((int *)data) = v; type = QMetaType::Int; } + int getint() const { return *((int *)data); } + + void setbool(bool v) { *((bool *)data) = v; type = QMetaType::Bool; } + bool getbool() const { return *((bool *)data); } + + QVariant *getvariantptr() { return (QVariant *)typeDataPtr(); } + QString *getstringptr() { return (QString *)typeDataPtr(); } + QUrl *geturlptr() { return (QUrl *)typeDataPtr(); } + const QVariant *getvariantptr() const { return (QVariant *)typeDataPtr(); } + const QString *getstringptr() const { return (QString *)typeDataPtr(); } + const QUrl *geturlptr() const { return (QUrl *)typeDataPtr(); } + + void *typeDataPtr() { return (void *)&data; } + void *typeMemory() { return (void *)data; } + const void *typeDataPtr() const { return (void *)&data; } + const void *typeMemory() const { return (void *)data; } + + int gettype() const { return type; } + void settype(int t) { type = t; } + + int type; // Optional type + void *data[2]; // Object stored here +}; +} + +class QmlCompiledBindingsPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QmlCompiledBindings) + +public: + QmlCompiledBindingsPrivate(); + virtual ~QmlCompiledBindingsPrivate(); + + struct Binding : public QmlAbstractBinding, public QmlDelayedError { + 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; + + QmlCompiledBindingsPrivate *parent; + }; + + struct Subscription { + struct Signal { + QmlGuard source; + int notifyIndex; + }; + + enum { InvalidType, SignalType, IdType } type; + inline Subscription(); + inline ~Subscription(); + bool isSignal() const { return type == SignalType; } + bool isId() const { return type == IdType; } + inline Signal *signal(); + inline QmlContextPrivate::IdNotifier *id(); + union { + char signalData[sizeof(Signal)]; + char idData[sizeof(QmlContextPrivate::IdNotifier)]; + }; + }; + Subscription *subscriptions; + QScriptDeclarativeClass::PersistentIdentifier *identifiers; + + void run(Binding *); + + const char *programData; + Binding *m_bindings; + quint32 *m_signalTable; + + static int methodCount; + + void init(); + void run(int instr, QmlContextPrivate *context, + QmlDelayedError *error, QObject *scope, QObject *output); + + + inline void unsubscribe(int subIndex); + inline void subscribeId(QmlContextPrivate *p, int idIndex, int subIndex); + inline void subscribe(QObject *o, int notifyIndex, int subIndex); + + QmlPropertyCache::Data *findproperty(QObject *obj, + const QScriptDeclarativeClass::Identifier &name, + QmlEnginePrivate *enginePriv, + QmlPropertyCache::Data &local); + bool findproperty(QObject *obj, + Register *output, + QmlEnginePrivate *enginePriv, + int subIdx, + const QScriptDeclarativeClass::Identifier &name, + bool isTerminal); + void findgeneric(Register *output, // value output + int subIdx, // Subscription index in config + QmlContextPrivate *context, // Context to search in + const QScriptDeclarativeClass::Identifier &name, + bool isTerminal); +}; + +QmlCompiledBindingsPrivate::QmlCompiledBindingsPrivate() +: subscriptions(0), identifiers(0) +{ +} + +QmlCompiledBindingsPrivate::~QmlCompiledBindingsPrivate() +{ + delete [] subscriptions; subscriptions = 0; + delete [] identifiers; identifiers = 0; +} + +QmlCompiledBindingsPrivate::Subscription::Subscription() +: type(InvalidType) +{ +} + +QmlCompiledBindingsPrivate::Subscription::~Subscription() +{ + if (type == SignalType) ((Signal *)signalData)->~Signal(); + else if (type == IdType) ((QmlContextPrivate::IdNotifier *)idData)->~IdNotifier(); +} + + +int QmlCompiledBindingsPrivate::methodCount = -1; + +QmlCompiledBindings::QmlCompiledBindings(const char *program, QmlContext *context) +: QObject(*(new QmlCompiledBindingsPrivate)) +{ + Q_D(QmlCompiledBindings); + + if (d->methodCount == -1) + d->methodCount = QmlCompiledBindings::staticMetaObject.methodCount(); + + d->programData = program; + + d->init(); + + QmlAbstractExpression::setContext(context); +} + +QmlCompiledBindings::~QmlCompiledBindings() +{ + Q_D(QmlCompiledBindings); + + delete [] d->m_bindings; +} + +QmlAbstractBinding *QmlCompiledBindings::configBinding(int index, QObject *target, + QObject *scope, int property) +{ + Q_D(QmlCompiledBindings); + + QmlCompiledBindingsPrivate::Binding *rv = d->m_bindings + index; + + rv->index = index; + rv->property = property; + rv->target = target; + rv->scope = scope; + rv->parent = d; + + addref(); // This is decremented in Binding::destroy() + + return rv; +} + +void QmlCompiledBindingsPrivate::Binding::setEnabled(bool e, QmlMetaProperty::WriteFlags flags) +{ + if (e) { + addToObject(target); + update(flags); + } else { + removeFromObject(); + } + + QmlAbstractBinding::setEnabled(e, flags); + + if (enabled != e) { + enabled = e; + + if (e) update(flags); + } +} + +int QmlCompiledBindingsPrivate::Binding::propertyIndex() +{ + return property & 0xFFFF; +} + +void QmlCompiledBindingsPrivate::Binding::update(QmlMetaProperty::WriteFlags) +{ + parent->run(this); +} + +void QmlCompiledBindingsPrivate::Binding::destroy() +{ + enabled = false; + removeFromObject(); + parent->q_func()->release(); +} + +int QmlCompiledBindings::qt_metacall(QMetaObject::Call c, int id, void **) +{ + Q_D(QmlCompiledBindings); + + if (c == QMetaObject::InvokeMetaMethod && id >= d->methodCount) { + id -= d->methodCount; + + quint32 *reeval = d->m_signalTable + d->m_signalTable[id]; + quint32 count = *reeval; + ++reeval; + for (quint32 ii = 0; ii < count; ++ii) { + d->run(d->m_bindings + reeval[ii]); + } + } + return -1; +} + +void QmlCompiledBindingsPrivate::run(Binding *binding) +{ + Q_Q(QmlCompiledBindings); + + if (!binding->enabled) + return; + if (binding->updating) + qWarning("ERROR: Circular binding"); + + QmlContext *context = q->QmlAbstractExpression::context(); + if (!context) { + qWarning("QmlCompiledBindings: Attempted to evaluate an expression in an invalid context"); + return; + } + QmlContextPrivate *cp = QmlContextPrivate::get(context); + + if (binding->property & 0xFFFF0000) { + QmlEnginePrivate *ep = QmlEnginePrivate::get(cp->engine); + + QmlValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF]; + Q_ASSERT(vt); + vt->read(binding->target, binding->property & 0xFFFF); + + QObject *target = vt; + run(binding->index, cp, binding, binding->scope, target); + + vt->write(binding->target, binding->property & 0xFFFF, + QmlMetaProperty::DontRemoveBinding); + } else { + run(binding->index, cp, binding, binding->scope, binding->target); + } +} + +QmlCompiledBindingsPrivate::Subscription::Signal *QmlCompiledBindingsPrivate::Subscription::signal() +{ + if (type == IdType) ((QmlContextPrivate::IdNotifier *)idData)->~IdNotifier(); + if (type != SignalType) new (signalData) Signal; + type = SignalType; + return (Signal *)signalData; +} + +QmlContextPrivate::IdNotifier *QmlCompiledBindingsPrivate::Subscription::id() +{ + if (type == SignalType) ((Signal *)signalData)->~Signal(); + if (type != IdType) new (idData) QmlContextPrivate::IdNotifier; + type = IdType; + return (QmlContextPrivate::IdNotifier *)idData; +} + +namespace { +// This structure is exactly 8-bytes in size +struct Instr { + enum { + Noop, + + Subscribe, // subscribe + SubscribeId, // subscribe + + LoadId, // load + LoadScope, // load + LoadRoot, // load + LoadAttached, // attached + + ConvertIntToReal, // unaryop + ConvertRealToInt, // unaryop + + Real, // real_value + Int, // int_value + Bool, // bool_value + String, // string_value + + AddReal, // binaryop + AddInt, // binaryop + AddString, // binaryop + + MinusReal, // binaryop + MinusInt, // binaryop + + CompareReal, // binaryop + CompareString, // binaryop + + NotCompareReal, // binaryop + NotCompareString, // binaryop + + GreaterThanReal, // binaryop + MaxReal, // binaryop + MinReal, // binaryop + + NewString, // construct + NewUrl, // construct + + CleanupUrl, // cleanup + CleanupString, // cleanup + + Copy, // copy + Fetch, // fetch + Store, // store + + Skip, // skip + + Done, + + // Speculative property resolution + InitString, // initstring + FindGeneric, // find + FindGenericTerminal, // find + FindProperty, // find + FindPropertyTerminal, // find + CleanupGeneric, // cleanup + ConvertGenericToReal, // unaryop + ConvertGenericToBool, // unaryop + ConvertGenericToString, // unaryop + ConvertGenericToUrl, // unaryop + }; + + union { + struct { + quint8 type; + quint8 packing[7]; + } common; + struct { + quint8 type; + quint8 packing[3]; + quint16 subscriptions; + quint16 identifiers; + } init; + struct { + quint8 type; + qint8 reg; + quint16 offset; + quint32 index; + } subscribe; + struct { + quint8 type; + qint8 reg; + quint8 packing[2]; + quint32 index; + } load; + struct { + quint8 type; + qint8 output; + qint8 reg; + quint8 exceptionId; + quint32 index; + } attached; + struct { + quint8 type; + qint8 output; + qint8 reg; + quint8 exceptionId; + quint32 index; + } store; + struct { + quint8 type; + qint8 output; + qint8 objectReg; + quint8 exceptionId; + quint32 index; + } fetch; + struct { + quint8 type; + qint8 reg; + qint8 src; + quint8 packing[5]; + } copy; + struct { + quint8 type; + qint8 reg; + quint8 packing[6]; + } construct; + struct { + quint8 type; + qint8 reg; + quint8 packing[2]; + float value; + } real_value; + struct { + quint8 type; + qint8 reg; + quint8 packing[2]; + int value; + } int_value; + struct { + quint8 type; + qint8 reg; + bool value; + quint8 packing[5]; + } bool_value; + struct { + quint8 type; + qint8 reg; + quint16 length; + quint32 offset; + } string_value; + struct { + quint8 type; + qint8 output; + qint8 src1; + qint8 src2; + quint8 packing[4]; + } binaryop; + struct { + quint8 type; + qint8 output; + qint8 src; + quint8 packing[5]; + } unaryop; + struct { + quint8 type; + qint8 reg; + quint8 packing[2]; + quint32 count; + } skip; + struct { + quint8 type; + qint8 reg; + qint8 src; + quint8 exceptionId; + quint16 name; + quint16 subscribeIndex; + } find; + struct { + quint8 type; + qint8 reg; + quint8 packing[6]; + } cleanup; + struct { + quint8 type; + quint8 packing[1]; + quint16 offset; + quint32 dataIdx; + } initstring; + }; +}; + +struct Program { + quint32 bindings; + quint32 dataLength; + quint32 signalTableOffset; + quint32 exceptionDataOffset; + quint16 subscriptions; + quint16 identifiers; + quint16 instructionCount; + quint16 dummy; + + const char *data() const { return ((const char *)this) + sizeof(Program); } + const Instr *instructions() const { return (const Instr *)(data() + dataLength); } +}; +} + +struct QmlBindingCompilerPrivate +{ + struct Result { + Result() : unknownType(false), metaObject(0), type(-1), reg(-1) {} + bool operator==(const Result &o) const { + return unknownType == o.unknownType && + metaObject == o.metaObject && + type == o.type && + reg == o.reg; + } + bool operator!=(const Result &o) const { + return !(*this == o); + } + bool unknownType; + const QMetaObject *metaObject; + int type; + int reg; + + QSet subscriptionSet; + }; + + QmlBindingCompilerPrivate() : registers(0) {} + + void resetInstanceState(); + int commitCompile(); + + QmlParser::Object *context; + QmlParser::Object *component; + QmlParser::Property *destination; + QHash ids; + 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 &); + + bool tryName(QmlJS::AST::Node *); + bool parseName(QmlJS::AST::Node *, Result &); + + bool tryArith(QmlJS::AST::Node *); + bool parseArith(QmlJS::AST::Node *, Result &); + bool numberArith(Result &, const Result &, const Result &, QSOperator::Op op); + bool stringArith(Result &, const Result &, const Result &, QSOperator::Op op); + + bool tryLogic(QmlJS::AST::Node *); + bool parseLogic(QmlJS::AST::Node *, Result &); + + bool tryConditional(QmlJS::AST::Node *); + bool parseConditional(QmlJS::AST::Node *, Result &); + + bool tryConstant(QmlJS::AST::Node *); + bool parseConstant(QmlJS::AST::Node *, Result &); + + bool tryMethod(QmlJS::AST::Node *); + bool parseMethod(QmlJS::AST::Node *, Result &); + + bool buildName(QStringList &, QmlJS::AST::Node *, QList *nodes = 0); + bool fetch(Result &type, const QMetaObject *, int reg, int idx, const QStringList &, QmlJS::AST::ExpressionNode *); + + quint32 registers; + QHash > registerCleanups; + int acquireReg(int cleanup = Instr::Noop, int cleanupType = 0); + void registerCleanup(int reg, int cleanup, int cleanupType = 0); + void releaseReg(int); + + int registerLiteralString(const QString &); + int registerString(const QString &); + QHash > registeredStrings; + QByteArray data; + + bool subscription(const QStringList &, Result *); + int subscriptionIndex(const QStringList &); + bool subscriptionNeutral(const QSet &base, const QSet &lhs, const QSet &rhs); + + quint8 exceptionId(QmlJS::AST::ExpressionNode *); + QVector exceptions; + + QSet usedSubscriptionIds; + QSet subscriptionSet; + QHash subscriptionIds; + QVector bytecode; + + // Committed binding data + struct { + QList offsets; + QList > dependencies; + + QVector bytecode; + QByteArray data; + QHash subscriptionIds; + QVector exceptions; + + QHash > registeredStrings; + + int count() const { return offsets.count(); } + } committed; + + QByteArray buildSignalTable() const; + QByteArray buildExceptionData() const; +}; + +void QmlCompiledBindingsPrivate::unsubscribe(int subIndex) +{ + Q_Q(QmlCompiledBindings); + + QmlCompiledBindingsPrivate::Subscription *sub = (subscriptions + subIndex); + if (sub->isSignal()) { + QmlCompiledBindingsPrivate::Subscription::Signal *s = sub->signal(); + if (s->source) +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 2)) + QMetaObject::disconnectOne(s->source, s->notifyIndex, + q, methodCount + subIndex); +#else + // QTBUG-6781 + QMetaObject::disconnect(s->source, s->notifyIndex, + q, methodCount + subIndex); +#endif + } else if (sub->isId()) { + sub->id()->clear(); + } +} + +void QmlCompiledBindingsPrivate::subscribeId(QmlContextPrivate *p, int idIndex, int subIndex) +{ + Q_Q(QmlCompiledBindings); + + unsubscribe(subIndex); + + if (p->idValues[idIndex]) { + QmlCompiledBindingsPrivate::Subscription *sub = (subscriptions + subIndex); + QmlContextPrivate::IdNotifier *i = sub->id(); + + i->next = p->idValues[idIndex].bindings; + i->prev = &p->idValues[idIndex].bindings; + p->idValues[idIndex].bindings = i; + if (i->next) i->next->prev = &i->next; + + i->target = q; + i->methodIndex = methodCount + subIndex; + } +} + +void QmlCompiledBindingsPrivate::subscribe(QObject *o, int notifyIndex, int subIndex) +{ + Q_Q(QmlCompiledBindings); + + QmlCompiledBindingsPrivate::Subscription *sub = (subscriptions + subIndex); + + if (sub->isId()) + unsubscribe(subIndex); + + QmlCompiledBindingsPrivate::Subscription::Signal *s = sub->signal(); + if (o != s->source || notifyIndex != s->notifyIndex) { + if (s->source) +#if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 2)) + QMetaObject::disconnectOne(s->source, s->notifyIndex, + q, methodCount + subIndex); +#else + // QTBUG-6781 + QMetaObject::disconnect(s->source, s->notifyIndex, + q, methodCount + subIndex); +#endif + s->source = o; + s->notifyIndex = notifyIndex; + if (s->source && s->notifyIndex != -1) + QMetaObject::connect(s->source, s->notifyIndex, q, + methodCount + subIndex, Qt::DirectConnection); + } +} + +// Conversion functions - these MUST match the QtScript expression path +inline static qreal toReal(Register *reg, int type, bool *ok = 0) +{ + if (ok) *ok = true; + + if (type == QMetaType::QReal) { + return reg->getqreal(); + } else if (type == qMetaTypeId()) { + return reg->getvariantptr()->toReal(); + } else { + if (ok) *ok = false; + return 0; + } +} + +inline static QString toString(Register *reg, int type, bool *ok = 0) +{ + if (ok) *ok = true; + + if (type == QMetaType::QReal) { + return QString::number(reg->getqreal()); + } else if (type == QMetaType::Int) { + return QString::number(reg->getint()); + } else if (type == qMetaTypeId()) { + return reg->getvariantptr()->toString(); + } else if (type == QMetaType::QString) { + return *reg->getstringptr(); + } else { + if (ok) *ok = false; + return QString(); + } +} + +inline static bool toBool(Register *reg, int type, bool *ok = 0) +{ + if (ok) *ok = true; + + if (type == QMetaType::Bool) { + return reg->getbool(); + } else if (type == qMetaTypeId()) { + return reg->getvariantptr()->toBool(); + } else { + if (ok) *ok = false; + return false; + } +} + +inline static QUrl toUrl(Register *reg, int type, QmlContextPrivate *context, bool *ok = 0) +{ + if (ok) *ok = true; + + QUrl base; + if (type == qMetaTypeId()) { + QVariant *var = reg->getvariantptr(); + int vt = var->type(); + if (vt == QVariant::Url) { + base = var->toUrl(); + } else if (vt == QVariant::ByteArray) { + base = QUrl(QString::fromUtf8(var->toByteArray())); + } else if (vt == QVariant::String) { + base = QUrl(var->toString()); + } else { + if (ok) *ok = false; + return QUrl(); + } + } else if (type == QMetaType::QString) { + base = QUrl(*reg->getstringptr()); + } else { + if (ok) *ok = false; + return QUrl(); + } + + if (!base.isEmpty() && base.isRelative()) + return context->url.resolved(base); + else + return base; +} + +static QObject *variantToQObject(const QVariant &value, bool *ok) +{ + if (ok) *ok = true; + + if (value.userType() == QMetaType::QObjectStar) { + return qvariant_cast(value); + } else { + if (ok) *ok = false; + return 0; + } +} + +QmlPropertyCache::Data * +QmlCompiledBindingsPrivate::findproperty(QObject *obj, + const QScriptDeclarativeClass::Identifier &name, + QmlEnginePrivate *enginePriv, + QmlPropertyCache::Data &local) +{ + QmlPropertyCache *cache = 0; + QmlDeclarativeData *ddata = QmlDeclarativeData::get(obj); + if (ddata) + cache = ddata->propertyCache; + if (!cache) { + cache = enginePriv->cache(obj); + if (cache && ddata) { cache->addref(); ddata->propertyCache = cache; } + } + + QmlPropertyCache::Data *property = 0; + + if (cache) { + property = cache->property(name); + } else { + qWarning() << "QmlBindingVME: Slow search" << enginePriv->objectClass->toString(name); + local = QmlPropertyCache::create(obj->metaObject(), + enginePriv->objectClass->toString(name)); + if (local.isValid()) + property = &local; + } + + return property; +} + +bool QmlCompiledBindingsPrivate::findproperty(QObject *obj, Register *output, + QmlEnginePrivate *enginePriv, + int subIdx, const QScriptDeclarativeClass::Identifier &name, + bool isTerminal) +{ + if (!obj) { + output->setUndefined(); + return false; + } + + QmlPropertyCache::Data local; + QmlPropertyCache::Data *property = findproperty(obj, name, enginePriv, local); + + if (property) { + if (subIdx != -1) + subscribe(obj, property->notifyIndex, subIdx); + + if (property->flags & QmlPropertyCache::Data::IsQObjectDerived) { + void *args[] = { output->typeDataPtr(), 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); + output->settype(QMetaType::QObjectStar); + } else if (property->propType == qMetaTypeId()) { + QVariant v; + void *args[] = { &v, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); + + if (isTerminal) { + new (output->typeDataPtr()) QVariant(v); + output->settype(qMetaTypeId()); + } else { + bool ok; + output->setQObject(variantToQObject(v, &ok)); + if (!ok) + output->setUndefined(); + else + output->settype(QMetaType::QObjectStar); + } + + } else { + if (!isTerminal) { + output->setUndefined(); + } else if (property->propType == QMetaType::QReal) { + void *args[] = { output->typeDataPtr(), 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); + output->settype(QMetaType::QReal); + } else if (property->propType == QMetaType::Int) { + void *args[] = { output->typeDataPtr(), 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); + output->settype(QMetaType::Int); + } else if (property->propType == QMetaType::Bool) { + void *args[] = { output->typeDataPtr(), 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); + output->settype(QMetaType::Bool); + } else if (property->propType == QMetaType::QString) { + new (output->typeDataPtr()) QString(); + void *args[] = { output->typeDataPtr(), 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args); + output->settype(QMetaType::QString); + } else { + new (output->typeDataPtr()) + QVariant(obj->metaObject()->property(property->coreIndex).read(obj)); + output->settype(qMetaTypeId()); + } + } + + return true; + } else { + output->setUndefined(); + return false; + } +} + +void QmlCompiledBindingsPrivate::findgeneric(Register *output, + int subIdx, + QmlContextPrivate *context, + const QScriptDeclarativeClass::Identifier &name, + bool isTerminal) +{ + QmlEnginePrivate *enginePriv = QmlEnginePrivate::get(context->engine); + + while (context) { + + int contextPropertyIndex = context->propertyNames?context->propertyNames->value(name):-1; + + + if (contextPropertyIndex != -1) { + + if (subIdx != -1) + subscribe(QmlContextPrivate::get(context), contextPropertyIndex + context->notifyIndex, subIdx); + + if (contextPropertyIndex < context->idValueCount) { + output->setQObject(context->idValues[contextPropertyIndex]); + output->settype(QMetaType::QObjectStar); + } else { + const QVariant &value = context->propertyValues.at(contextPropertyIndex); + if (isTerminal) { + new (output->typeDataPtr()) QVariant(value); + output->settype(qMetaTypeId()); + } else { + bool ok; + output->setQObject(variantToQObject(value, &ok)); + if (!ok) { output->setUndefined(); } + else { output->settype(QMetaType::QObjectStar); } + return; + } + } + + return; + } + + for (int ii = 0; ii < context->scripts.count(); ++ii) { + QScriptValue function = QScriptDeclarativeClass::function(context->scripts.at(ii), name); + if (function.isValid()) { + qFatal("Binding optimizer resolved name to QScript method"); + } + } + + if (QObject *root = context->defaultObjects.isEmpty()?0:context->defaultObjects.first()) { + + if (findproperty(root, output, enginePriv, subIdx, name, isTerminal)) + return; + + } + + if (context->parent) { + context = QmlContextPrivate::get(context->parent); + } else { + context = 0; + } + } + + output->setUndefined(); +} + +void QmlCompiledBindingsPrivate::init() +{ + Program *program = (Program *)programData; + if (program->subscriptions) + subscriptions = new QmlCompiledBindingsPrivate::Subscription[program->subscriptions]; + if (program->identifiers) + identifiers = new QScriptDeclarativeClass::PersistentIdentifier[program->identifiers]; + + m_signalTable = (quint32 *)(program->data() + program->signalTableOffset); + m_bindings = new QmlCompiledBindingsPrivate::Binding[program->bindings]; +} + +static void throwException(int id, QmlDelayedError *error, + Program *program, QmlContextPrivate *context, + const QString &description = QString()) +{ + error->error.setUrl(context->url); + if (description.isEmpty()) + error->error.setDescription(QLatin1String("TypeError: Result of expression is not an object")); + else + error->error.setDescription(description); + if (id != 0xFF) { + quint64 e = *((quint64 *)(program->data() + program->exceptionDataOffset) + id); + error->error.setLine((e >> 32) & 0xFFFFFFFF); + error->error.setColumn(e & 0xFFFFFFFF); + } else { + error->error.setLine(-1); + error->error.setColumn(-1); + } + if (!context->engine || !error->addError(QmlEnginePrivate::get(context->engine))) + qWarning() << error->error; +} + +void QmlCompiledBindingsPrivate::run(int instrIndex, + QmlContextPrivate *context, QmlDelayedError *error, + QObject *scope, QObject *output) +{ + error->removeError(); + + Register registers[32]; + int storeFlags = 0; + + QmlEnginePrivate *engine = QmlEnginePrivate::get(context->engine); + Program *program = (Program *)programData; + const Instr *instr = program->instructions(); + instr += instrIndex; + const char *data = program->data(); + + while (instr) { + + switch (instr->common.type) { + case Instr::Noop: + break; + + case Instr::SubscribeId: + subscribeId(context, instr->subscribe.index, instr->subscribe.offset); + break; + + case Instr::Subscribe: + { + QObject *o = 0; + const Register &object = registers[instr->subscribe.reg]; + if (!object.isUndefined()) o = object.getQObject(); + subscribe(o, instr->subscribe.index, instr->subscribe.offset); + } + break; + + case Instr::LoadId: + registers[instr->load.reg].setQObject(context->idValues[instr->load.index].data()); + break; + + case Instr::LoadScope: + registers[instr->load.reg].setQObject(scope); + break; + + case Instr::LoadRoot: + registers[instr->load.reg].setQObject(context->defaultObjects.at(0)); + break; + + case Instr::LoadAttached: + { + const Register &input = registers[instr->attached.reg]; + Register &output = registers[instr->attached.output]; + if (input.isUndefined()) { + throwException(instr->attached.exceptionId, error, program, context); + return; + } + + QObject *object = registers[instr->attached.reg].getQObject(); + if (!object) { + output.setUndefined(); + } else { + QObject *attached = + qmlAttachedPropertiesObjectById(instr->attached.index, + registers[instr->attached.reg].getQObject(), + true); + Q_ASSERT(attached); + output.setQObject(attached); + } + } + break; + + case Instr::ConvertIntToReal: + { + const Register &input = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (input.isUndefined()) output.setUndefined(); + else output.setqreal(qreal(input.getint())); + } + break; + + case Instr::ConvertRealToInt: + { + const Register &input = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (input.isUndefined()) output.setUndefined(); + else output.setint(int(input.getqreal())); + } + break; + + case Instr::Real: + registers[instr->real_value.reg].setqreal(instr->real_value.value); + break; + + case Instr::Int: + registers[instr->int_value.reg].setint(instr->int_value.value); + break; + + case Instr::Bool: + registers[instr->bool_value.reg].setbool(instr->bool_value.value); + break; + + case Instr::String: + { + Register &output = registers[instr->string_value.reg]; + new (output.getstringptr()) + QString((QChar *)(data + instr->string_value.offset), instr->string_value.length); + output.settype(QMetaType::QString); + } + break; + + case Instr::AddReal: + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setqreal(lhs.getqreal() + rhs.getqreal()); + } + break; + + case Instr::AddInt: + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setint(lhs.getint() + rhs.getint()); + } + break; + + case Instr::AddString: + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() && rhs.isUndefined()) { output.setNaN(); } + else { + if (lhs.isUndefined()) + new (output.getstringptr()) + QString(QLatin1String("undefined") + *registers[instr->binaryop.src2].getstringptr()); + else if (rhs.isUndefined()) + new (output.getstringptr()) + QString(*registers[instr->binaryop.src1].getstringptr() + QLatin1String("undefined")); + else + new (output.getstringptr()) + QString(*registers[instr->binaryop.src1].getstringptr() + + *registers[instr->binaryop.src2].getstringptr()); + output.settype(QMetaType::QString); + } + } + break; + + case Instr::MinusReal: + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setqreal(lhs.getqreal() - rhs.getqreal()); + } + break; + + case Instr::MinusInt: + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setint(lhs.getint() - rhs.getint()); + } + break; + + case Instr::CompareReal: + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() == rhs.isUndefined()); + else output.setbool(lhs.getqreal() == rhs.getqreal()); + } + break; + + case Instr::CompareString: + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() == rhs.isUndefined()); + else output.setbool(*lhs.getstringptr() == *rhs.getstringptr()); + } + break; + + case Instr::NotCompareReal: + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() != rhs.isUndefined()); + else output.setbool(lhs.getqreal() != rhs.getqreal()); + } + break; + + case Instr::NotCompareString: + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() != rhs.isUndefined()); + else output.setbool(*lhs.getstringptr() != *rhs.getstringptr()); + } + break; + + case Instr::GreaterThanReal: + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(false); + else output.setbool(lhs.getqreal() > rhs.getqreal()); + } + break; + + case Instr::MaxReal: + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setqreal(qMax(lhs.getqreal(), rhs.getqreal())); + } + break; + + case Instr::MinReal: + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setqreal(qMin(lhs.getqreal(), rhs.getqreal())); + } + break; + + case Instr::NewString: + { + Register &output = registers[instr->construct.reg]; + new (output.getstringptr()) QString; + output.settype(QMetaType::QString); + } + break; + + case Instr::NewUrl: + { + Register &output = registers[instr->construct.reg]; + new (output.geturlptr()) QUrl; + output.settype(QMetaType::QUrl); + } + break; + + case Instr::CleanupString: + registers[instr->cleanup.reg].getstringptr()->~QString(); + break; + + case Instr::CleanupUrl: + registers[instr->cleanup.reg].geturlptr()->~QUrl(); + break; + + case Instr::Fetch: + { + const Register &input = registers[instr->fetch.objectReg]; + Register &output = registers[instr->fetch.output]; + + if (input.isUndefined()) { + throwException(instr->fetch.exceptionId, error, program, context); + return; + } + + QObject *object = input.getQObject(); + if (!object) { + output.setUndefined(); + } else { + void *argv[] = { output.typeDataPtr(), 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, instr->fetch.index, argv); + } + } + break; + + case Instr::Store: + { + Register &data = registers[instr->store.reg]; + if (data.isUndefined()) { + throwException(instr->store.exceptionId, error, program, context, + QLatin1String("Unable to assign undefined value")); + return; + } + + int status = -1; + void *argv[] = { data.typeDataPtr(), 0, &status, &storeFlags }; + QMetaObject::metacall(output, QMetaObject::WriteProperty, + instr->store.index, argv); + } + break; + + case Instr::Copy: + registers[instr->copy.reg] = registers[instr->copy.src]; + break; + + case Instr::Skip: + if (instr->skip.reg == -1 || !registers[instr->skip.reg].getbool()) + instr += instr->skip.count; + break; + + case Instr::Done: + return; + + case Instr::InitString: + if (!identifiers[instr->initstring.offset].identifier) { + quint32 len = *(quint32 *)(data + instr->initstring.dataIdx); + QChar *strdata = (QChar *)(data + instr->initstring.dataIdx + sizeof(quint32)); + + QString str = QString::fromRawData(strdata, len); + + identifiers[instr->initstring.offset] = engine->objectClass->createPersistentIdentifier(str); + } + break; + + case Instr::FindGenericTerminal: + case Instr::FindGeneric: + // We start the search in the parent context, as we know that the + // name is not present in the current context or it would have been + // found during the static compile + findgeneric(registers + instr->find.reg, instr->find.subscribeIndex, + QmlContextPrivate::get(context->parent), + identifiers[instr->find.name].identifier, + instr->common.type == Instr::FindGenericTerminal); + break; + + case Instr::FindPropertyTerminal: + case Instr::FindProperty: + { + const Register &object = registers[instr->find.src]; + if (object.isUndefined()) { + throwException(instr->find.exceptionId, error, program, context); + return; + } + + findproperty(object.getQObject(), registers + instr->find.reg, + QmlEnginePrivate::get(context->engine), + instr->find.subscribeIndex, identifiers[instr->find.name].identifier, + instr->common.type == Instr::FindPropertyTerminal); + } + break; + + case Instr::CleanupGeneric: + { + int type = registers[instr->cleanup.reg].gettype(); + if (type == qMetaTypeId()) { + registers[instr->cleanup.reg].getvariantptr()->~QVariant(); + } else if (type == QMetaType::QString) { + registers[instr->cleanup.reg].getstringptr()->~QString(); + } else if (type == QMetaType::QUrl) { + registers[instr->cleanup.reg].geturlptr()->~QUrl(); + } + } + break; + + case Instr::ConvertGenericToReal: + { + Register &output = registers[instr->unaryop.output]; + Register &input = registers[instr->unaryop.src]; + bool ok = true; + output.setqreal(toReal(&input, input.gettype(), &ok)); + if (!ok) output.setUndefined(); + } + break; + + case Instr::ConvertGenericToBool: + { + Register &output = registers[instr->unaryop.output]; + Register &input = registers[instr->unaryop.src]; + bool ok = true; + output.setbool(toBool(&input, input.gettype(), &ok)); + if (!ok) output.setUndefined(); + } + break; + + case Instr::ConvertGenericToString: + { + Register &output = registers[instr->unaryop.output]; + Register &input = registers[instr->unaryop.src]; + bool ok = true; + QString str = toString(&input, input.gettype(), &ok); + if (ok) { new (output.getstringptr()) QString(str); output.settype(QMetaType::QString); } + else { output.setUndefined(); } + } + break; + + case Instr::ConvertGenericToUrl: + { + Register &output = registers[instr->unaryop.output]; + Register &input = registers[instr->unaryop.src]; + bool ok = true; + QUrl url = toUrl(&input, input.gettype(), context, &ok); + if (ok) { new (output.geturlptr()) QUrl(url); output.settype(QMetaType::QUrl); } + else { output.setUndefined(); } + } + break; + + default: + qFatal("EEK"); + break; + } + + instr++; + } +} + +void QmlBindingCompiler::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 = program->instructionCount; + const Instr *instr = program->instructions(); + + while (count--) { + + switch (instr->common.type) { + case Instr::Noop: + qWarning().nospace() << "Noop"; + break; + case Instr::Subscribe: + qWarning().nospace() << "Subscribe" << "\t\t" << instr->subscribe.offset << "\t" << instr->subscribe.reg << "\t" << instr->subscribe.index; + break; + case Instr::SubscribeId: + qWarning().nospace() << "SubscribeId" << "\t\t" << instr->subscribe.offset << "\t" << instr->subscribe.reg << "\t" << instr->subscribe.index; + break; + case Instr::LoadId: + qWarning().nospace() << "LoadId" << "\t\t\t" << instr->load.index << "\t" << instr->load.reg; + break; + case Instr::LoadScope: + qWarning().nospace() << "LoadScope" << "\t\t" << instr->load.index << "\t" << instr->load.reg; + break; + case Instr::LoadRoot: + qWarning().nospace() << "LoadRoot" << "\t\t" << instr->load.index << "\t" << instr->load.reg; + break; + case Instr::LoadAttached: + qWarning().nospace() << "LoadAttached" << "\t\t" << instr->attached.output << "\t" << instr->attached.reg << "\t" << instr->attached.index; + break; + case Instr::ConvertIntToReal: + qWarning().nospace() << "ConvertIntToReal" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; + break; + case Instr::ConvertRealToInt: + qWarning().nospace() << "ConvertRealToInt" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; + break; + case Instr::Real: + qWarning().nospace() << "Real" << "\t\t\t" << instr->real_value.reg << "\t" << instr->real_value.value; + break; + case Instr::Int: + qWarning().nospace() << "Int" << "\t\t\t" << instr->int_value.reg << "\t" << instr->int_value.value; + break; + case Instr::Bool: + qWarning().nospace() << "Bool" << "\t\t\t" << instr->bool_value.reg << "\t" << instr->bool_value.value; + break; + case Instr::String: + qWarning().nospace() << "String" << "\t\t\t" << instr->string_value.reg << "\t" << instr->string_value.offset << "\t" << instr->string_value.length; + break; + case Instr::AddReal: + qWarning().nospace() << "AddReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::AddInt: + qWarning().nospace() << "AddInt" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::AddString: + qWarning().nospace() << "AddString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::MinusReal: + qWarning().nospace() << "MinusReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::MinusInt: + qWarning().nospace() << "MinusInt" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::CompareReal: + qWarning().nospace() << "CompareReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::CompareString: + qWarning().nospace() << "CompareString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::NotCompareReal: + qWarning().nospace() << "NotCompareReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::NotCompareString: + qWarning().nospace() << "NotCompareString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::GreaterThanReal: + qWarning().nospace() << "GreaterThanReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::MaxReal: + qWarning().nospace() << "MaxReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::MinReal: + qWarning().nospace() << "MinReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2; + break; + case Instr::NewString: + qWarning().nospace() << "NewString" << "\t\t" << instr->construct.reg; + break; + case Instr::NewUrl: + qWarning().nospace() << "NewUrl" << "\t\t\t" << instr->construct.reg; + break; + case Instr::CleanupString: + qWarning().nospace() << "CleanupString" << "\t\t" << instr->cleanup.reg; + break; + case Instr::CleanupUrl: + qWarning().nospace() << "CleanupUrl" << "\t\t" << instr->cleanup.reg; + break; + case Instr::Fetch: + qWarning().nospace() << "Fetch" << "\t\t\t" << instr->fetch.output << "\t" << instr->fetch.index << "\t" << instr->fetch.objectReg; + break; + case Instr::Store: + qWarning().nospace() << "Store" << "\t\t\t" << instr->store.output << "\t" << instr->store.index << "\t" << instr->store.reg; + break; + case Instr::Copy: + qWarning().nospace() << "Copy" << "\t\t\t" << instr->copy.reg << "\t" << instr->copy.src; + break; + case Instr::Skip: + qWarning().nospace() << "Skip" << "\t\t\t" << instr->skip.reg << "\t" << instr->skip.count; + break; + case Instr::Done: + qWarning().nospace() << "Done"; + break; + case Instr::InitString: + qWarning().nospace() << "InitString" << "\t\t" << instr->initstring.offset << "\t" << instr->initstring.dataIdx; + break; + case Instr::FindGeneric: + qWarning().nospace() << "FindGeneric" << "\t\t" << instr->find.reg << "\t" << instr->find.name; + break; + case Instr::FindGenericTerminal: + qWarning().nospace() << "FindGenericTerminal" << "\t" << instr->find.reg << "\t" << instr->find.name; + break; + case Instr::FindProperty: + qWarning().nospace() << "FindProperty" << "\t\t" << instr->find.reg << "\t" << instr->find.src << "\t" << instr->find.name; + break; + case Instr::FindPropertyTerminal: + qWarning().nospace() << "FindPropertyTerminal" << "\t" << instr->find.reg << "\t" << instr->find.src << "\t" << instr->find.name; + break; + case Instr::CleanupGeneric: + qWarning().nospace() << "CleanupGeneric" << "\t\t" << instr->cleanup.reg; + break; + case Instr::ConvertGenericToReal: + qWarning().nospace() << "ConvertGenericToReal" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; + break; + case Instr::ConvertGenericToBool: + qWarning().nospace() << "ConvertGenericToBool" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; + break; + case Instr::ConvertGenericToString: + qWarning().nospace() << "ConvertGenericToString" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; + break; + case Instr::ConvertGenericToUrl: + qWarning().nospace() << "ConvertGenericToUrl" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src; + break; + default: + qWarning().nospace() << "Unknown"; + break; + } + + ++instr; + } +} + +/*! +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; + registerCleanups.clear(); + data = committed.data; + exceptions = committed.exceptions; + usedSubscriptionIds.clear(); + subscriptionSet.clear(); + subscriptionIds = committed.subscriptionIds; + registeredStrings = committed.registeredStrings; + bytecode.clear(); +} + +/*! +Mark the last compile as successful, and add it to the "committed data" +section. + +Returns the index for the committed binding. +*/ +int QmlBindingCompilerPrivate::commitCompile() +{ + int rv = committed.count(); + committed.offsets << committed.bytecode.count(); + committed.dependencies << usedSubscriptionIds; + committed.bytecode << bytecode; + committed.data = data; + committed.exceptions = exceptions; + committed.subscriptionIds = subscriptionIds; + committed.registeredStrings = registeredStrings; + return rv; +} + +bool QmlBindingCompilerPrivate::compile(QmlJS::AST::Node *node) +{ + resetInstanceState(); + + Result type; + + if (!parseExpression(node, type)) + return false; + + if (subscriptionSet.count() > 0xFFFF || + registeredStrings.count() > 0xFFFF) + return false; + + if (type.unknownType) { + if (destination->type != QMetaType::QReal && + destination->type != QVariant::String && + destination->type != QMetaType::Bool && + destination->type != QVariant::Url) + return false; + + int convertReg = acquireReg(); + + if (destination->type == QMetaType::QReal) { + Instr convert; + convert.common.type = Instr::ConvertGenericToReal; + convert.unaryop.output = convertReg; + convert.unaryop.src = type.reg; + bytecode << convert; + } else if (destination->type == QVariant::String) { + Instr convert; + convert.common.type = Instr::ConvertGenericToString; + convert.unaryop.output = convertReg; + convert.unaryop.src = type.reg; + bytecode << convert; + } else if (destination->type == QMetaType::Bool) { + Instr convert; + convert.common.type = Instr::ConvertGenericToBool; + convert.unaryop.output = convertReg; + convert.unaryop.src = type.reg; + bytecode << convert; + } else if (destination->type == QVariant::Url) { + Instr convert; + convert.common.type = Instr::ConvertGenericToUrl; + convert.unaryop.output = convertReg; + convert.unaryop.src = type.reg; + bytecode << convert; + } + + Instr cleanup; + cleanup.common.type = Instr::CleanupGeneric; + cleanup.cleanup.reg = type.reg; + bytecode << cleanup; + + Instr instr; + instr.common.type = Instr::Store; + instr.store.output = 0; + instr.store.index = destination->index; + instr.store.reg = convertReg; + instr.store.exceptionId = exceptionId(node->expressionCast()); + bytecode << instr; + + if (destination->type == QVariant::String) { + Instr cleanup; + cleanup.common.type = Instr::CleanupString; + cleanup.cleanup.reg = convertReg; + bytecode << cleanup; + } else if (destination->type == QVariant::Url) { + Instr cleanup; + cleanup.common.type = Instr::CleanupUrl; + cleanup.cleanup.reg = convertReg; + bytecode << cleanup; + } + + releaseReg(convertReg); + + Instr done; + done.common.type = Instr::Done; + bytecode << done; + + return true; + } else { + // Can we store the final value? + if (type.type == QVariant::Int && + destination->type == QMetaType::QReal) { + Instr instr; + instr.common.type = Instr::ConvertIntToReal; + instr.unaryop.output = type.reg; + instr.unaryop.src = type.reg; + bytecode << instr; + type.type = QMetaType::QReal; + } else if (type.type == QMetaType::QReal && + destination->type == QVariant::Int) { + Instr instr; + instr.common.type = Instr::ConvertRealToInt; + instr.unaryop.output = type.reg; + instr.unaryop.src = type.reg; + bytecode << instr; + type.type = QVariant::Int; + } else if (type.type == destination->type) { + } else { + const QMetaObject *from = type.metaObject; + const QMetaObject *to = QmlMetaType::rawMetaObjectForType(destination->type); + + if (QmlMetaPropertyPrivate::canConvert(from, to)) + type.type = destination->type; + } + + if (type.type == destination->type) { + Instr instr; + instr.common.type = Instr::Store; + instr.store.output = 0; + instr.store.index = destination->index; + instr.store.reg = type.reg; + instr.store.exceptionId = exceptionId(node->expressionCast()); + bytecode << instr; + + releaseReg(type.reg); + + Instr done; + done.common.type = Instr::Done; + bytecode << done; + + return true; + } else { + return false; + } + } +} + +bool QmlBindingCompilerPrivate::parseExpression(QmlJS::AST::Node *node, Result &type) +{ + while (node->kind == AST::Node::Kind_NestedExpression) + node = static_cast(node)->expression; + + if (tryArith(node)) { + if (!parseArith(node, type)) return false; + } else if (tryLogic(node)) { + if (!parseLogic(node, type)) return false; + } else if (tryConditional(node)) { + if (!parseConditional(node, type)) return false; + } else if (tryName(node)) { + if (!parseName(node, type)) return false; + } else if (tryConstant(node)) { + if (!parseConstant(node, type)) return false; + } else if (tryMethod(node)) { + if (!parseMethod(node, type)) return false; + } else { + return false; + } + return true; +} + +bool QmlBindingCompilerPrivate::tryName(QmlJS::AST::Node *node) +{ + return node->kind == AST::Node::Kind_IdentifierExpression || + node->kind == AST::Node::Kind_FieldMemberExpression; +} + +bool QmlBindingCompilerPrivate::parseName(AST::Node *node, Result &type) +{ + QStringList nameParts; + QList nameNodes; + if (!buildName(nameParts, node, &nameNodes)) + return false; + + int reg = acquireReg(); + if (reg == -1) + return false; + type.reg = reg; + + QmlParser::Object *absType = 0; + + QStringList subscribeName; + + bool wasAttachedObject = false; + + for (int ii = 0; ii < nameParts.count(); ++ii) { + const QString &name = nameParts.at(ii); + + // We don't handle signal properties or attached properties + if (name.length() > 2 && name.startsWith(QLatin1String("on")) && + name.at(2).isUpper()) + return false; + + QmlType *attachType = 0; + if (name.at(0).isUpper()) { + // Could be an attached property + if (ii == nameParts.count() - 1) + return false; + if (nameParts.at(ii + 1).at(0).isUpper()) + return false; + + QmlEnginePrivate::ImportedNamespace *ns = 0; + if (!engine->resolveType(imports, name.toUtf8(), &attachType, 0, 0, 0, &ns)) + return false; + if (ns || !attachType || !attachType->attachedPropertiesType()) + return false; + + wasAttachedObject = true; + } + + if (ii == 0) { + + if (attachType) { + Instr instr; + instr.common.type = Instr::LoadScope; + instr.load.index = 0; + instr.load.reg = reg; + bytecode << instr; + + Instr attach; + attach.common.type = Instr::LoadAttached; + attach.attached.output = reg; + attach.attached.reg = reg; + attach.attached.index = attachType->index(); + attach.attached.exceptionId = exceptionId(nameNodes.at(ii)); + bytecode << attach; + + subscribeName << contextName(); + subscribeName << QLatin1String("$$$ATTACH_") + name; + + absType = 0; + type.metaObject = attachType->attachedPropertiesType(); + + continue; + } else if (ids.contains(name)) { + QmlParser::Object *idObject = ids.value(name); + absType = idObject; + type.metaObject = absType->metaObject(); + + // We check if the id object is the root or + // scope object to avoid a subscription + if (idObject == component) { + Instr instr; + instr.common.type = Instr::LoadRoot; + instr.load.index = 0; + instr.load.reg = reg; + bytecode << instr; + } else if (idObject == context) { + Instr instr; + instr.common.type = Instr::LoadScope; + instr.load.index = 0; + instr.load.reg = reg; + bytecode << instr; + } else { + Instr instr; + instr.common.type = Instr::LoadId; + instr.load.index = idObject->idIndex; + instr.load.reg = reg; + bytecode << instr; + + subscribeName << QLatin1String("$$$ID_") + name; + + if (subscription(subscribeName, &type)) { + Instr sub; + sub.common.type = Instr::SubscribeId; + sub.subscribe.offset = subscriptionIndex(subscribeName); + sub.subscribe.reg = reg; + sub.subscribe.index = instr.load.index; + bytecode << sub; + } + } + + } else { + + QByteArray utf8Name = name.toUtf8(); + const char *cname = utf8Name.constData(); + + int d0Idx = (context == component)?-1:context->metaObject()->indexOfProperty(cname); + int d1Idx = -1; + if (d0Idx == -1) + d1Idx = component->metaObject()->indexOfProperty(cname); + + if (d0Idx != -1) { + Instr instr; + instr.common.type = Instr::LoadScope; + instr.load.index = 0; + instr.load.reg = reg; + bytecode << instr; + + subscribeName << contextName(); + subscribeName << name; + + fetch(type, context->metaObject(), reg, d0Idx, subscribeName, nameNodes.at(ii)); + } else if(d1Idx != -1) { + Instr instr; + instr.common.type = Instr::LoadRoot; + instr.load.index = 0; + instr.load.reg = reg; + bytecode << instr; + + subscribeName << QLatin1String("$$$ROOT"); + subscribeName << name; + + fetch(type, component->metaObject(), reg, d1Idx, subscribeName, nameNodes.at(ii)); + } else { + Instr find; + if (nameParts.count() == 1) + find.common.type = Instr::FindGenericTerminal; + else + find.common.type = Instr::FindGeneric; + + find.find.reg = reg; + find.find.src = -1; + find.find.name = registerString(name); + find.find.exceptionId = exceptionId(nameNodes.at(ii)); + + subscribeName << QString(QLatin1String("$$$Generic_") + name); + if (subscription(subscribeName, &type)) + find.find.subscribeIndex = subscriptionIndex(subscribeName); + else + find.find.subscribeIndex = -1; + + bytecode << find; + type.unknownType = true; + } + + if (!type.unknownType && type.type == -1) + return false; // Couldn't fetch that type + } + + } else { + + if (attachType) { + Instr attach; + attach.common.type = Instr::LoadAttached; + attach.attached.output = reg; + attach.attached.reg = reg; + attach.attached.index = attachType->index(); + bytecode << attach; + + absType = 0; + type.metaObject = attachType->attachedPropertiesType(); + + subscribeName << QLatin1String("$$$ATTACH_") + name; + continue; + } + + const QMetaObject *mo = 0; + if (absType) + mo = absType->metaObject(); + else if (type.metaObject) + mo = type.metaObject; + + QByteArray utf8Name = name.toUtf8(); + const char *cname = utf8Name.constData(); + int idx = mo?mo->indexOfProperty(cname):-1; + if (absType && idx == -1) + return false; + + subscribeName << name; + + if (absType || (wasAttachedObject && idx != -1) || (mo && mo->property(idx).isFinal())) { + absType = 0; + fetch(type, mo, reg, idx, subscribeName, nameNodes.at(ii)); + if (type.type == -1) + return false; + } else { + + Instr prop; + if (ii == nameParts.count() -1 ) + prop.common.type = Instr::FindPropertyTerminal; + else + prop.common.type = Instr::FindProperty; + + prop.find.reg = reg; + prop.find.src = reg; + prop.find.name = registerString(name); + prop.find.exceptionId = exceptionId(nameNodes.at(ii)); + + if (subscription(subscribeName, &type)) + prop.find.subscribeIndex = subscriptionIndex(subscribeName); + else + prop.find.subscribeIndex = -1; + + type.unknownType = true; + type.metaObject = 0; + type.type = -1; + type.reg = reg; + bytecode << prop; + } + } + + wasAttachedObject = false; + } + + return true; +} + +bool QmlBindingCompilerPrivate::tryArith(QmlJS::AST::Node *node) +{ + if (node->kind != AST::Node::Kind_BinaryExpression) + return false; + + AST::BinaryExpression *expression = static_cast(node); + if (expression->op == QSOperator::Add || + expression->op == QSOperator::Sub) + return true; + else + return false; +} + +bool QmlBindingCompilerPrivate::parseArith(QmlJS::AST::Node *node, Result &type) +{ + AST::BinaryExpression *expression = static_cast(node); + + type.reg = acquireReg(); + + Result lhs; + Result rhs; + + if (!parseExpression(expression->left, lhs)) return false; + if (!parseExpression(expression->right, rhs)) return false; + + if ((lhs.type == QVariant::Int || lhs.type == QMetaType::QReal) && + (rhs.type == QVariant::Int || rhs.type == QMetaType::QReal)) + return numberArith(type, lhs, rhs, (QSOperator::Op)expression->op); + else if(expression->op == QSOperator::Sub) + return numberArith(type, lhs, rhs, (QSOperator::Op)expression->op); + else if ((lhs.type == QMetaType::QString || lhs.unknownType) && + (rhs.type == QMetaType::QString || rhs.unknownType) && + (lhs.type == QMetaType::QString || rhs.type == QMetaType::QString)) + return stringArith(type, lhs, rhs, (QSOperator::Op)expression->op); + else + return false; +} + +bool QmlBindingCompilerPrivate::numberArith(Result &type, const Result &lhs, const Result &rhs, QSOperator::Op op) +{ + bool nativeReal = rhs.type == QMetaType::QReal || + lhs.type == QMetaType::QReal || + lhs.unknownType || + rhs.unknownType; + + if (nativeReal && lhs.type == QMetaType::Int) { + Instr convert; + convert.common.type = Instr::ConvertIntToReal; + convert.unaryop.output = lhs.reg; + convert.unaryop.src = lhs.reg; + bytecode << convert; + } + + if (nativeReal && rhs.type == QMetaType::Int) { + Instr convert; + convert.common.type = Instr::ConvertIntToReal; + convert.unaryop.output = rhs.reg; + convert.unaryop.src = rhs.reg; + bytecode << convert; + } + + int lhsTmp = -1; + int rhsTmp = -1; + if (lhs.unknownType) { + lhsTmp = acquireReg(); + + Instr conv; + conv.common.type = Instr::ConvertGenericToReal; + conv.unaryop.output = lhsTmp; + conv.unaryop.src = lhs.reg; + bytecode << conv; + } + + if (rhs.unknownType) { + rhsTmp = acquireReg(); + + Instr conv; + conv.common.type = Instr::ConvertGenericToReal; + conv.unaryop.output = rhsTmp; + conv.unaryop.src = rhs.reg; + bytecode << conv; + } + + Instr arith; + if (op == QSOperator::Add) { + arith.common.type = nativeReal?Instr::AddReal:Instr::AddInt; + } else if (op == QSOperator::Sub) { + arith.common.type = nativeReal?Instr::MinusReal:Instr::MinusInt; + } else { + qFatal("Unsupported arithmetic operator"); + } + + arith.binaryop.output = type.reg; + arith.binaryop.src1 = (lhsTmp == -1)?lhs.reg:lhsTmp; + arith.binaryop.src2 = (rhsTmp == -1)?rhs.reg:rhsTmp; + bytecode << arith; + + type.metaObject = 0; + type.type = nativeReal?QMetaType::QReal:QMetaType::Int; + type.subscriptionSet.unite(lhs.subscriptionSet); + type.subscriptionSet.unite(rhs.subscriptionSet); + + if (lhsTmp != -1) releaseReg(lhsTmp); + if (rhsTmp != -1) releaseReg(rhsTmp); + releaseReg(lhs.reg); + releaseReg(rhs.reg); + + return true; +} + +bool QmlBindingCompilerPrivate::stringArith(Result &type, const Result &lhs, const Result &rhs, QSOperator::Op op) +{ + if (op != QSOperator::Add) + return false; + + int lhsTmp = -1; + int rhsTmp = -1; + + if (lhs.unknownType) { + lhsTmp = acquireReg(Instr::CleanupString); + + Instr convert; + convert.common.type = Instr::ConvertGenericToString; + convert.unaryop.output = lhsTmp; + convert.unaryop.src = lhs.reg; + bytecode << convert; + } + + if (rhs.unknownType) { + rhsTmp = acquireReg(Instr::CleanupString); + + Instr convert; + convert.common.type = Instr::ConvertGenericToString; + convert.unaryop.output = rhsTmp; + convert.unaryop.src = rhs.reg; + bytecode << convert; + } + + type.reg = acquireReg(Instr::CleanupString); + type.type = QMetaType::QString; + + Instr add; + add.common.type = Instr::AddString; + add.binaryop.output = type.reg; + add.binaryop.src1 = (lhsTmp == -1)?lhs.reg:lhsTmp; + add.binaryop.src2 = (rhsTmp == -1)?rhs.reg:rhsTmp; + bytecode << add; + + if (lhsTmp != -1) releaseReg(lhsTmp); + if (rhsTmp != -1) releaseReg(rhsTmp); + + return true; +} + +bool QmlBindingCompilerPrivate::tryLogic(QmlJS::AST::Node *node) +{ + if (node->kind != AST::Node::Kind_BinaryExpression) + return false; + + AST::BinaryExpression *expression = static_cast(node); + if (expression->op == QSOperator::Gt || + expression->op == QSOperator::Equal || + expression->op == QSOperator::NotEqual) + return true; + else + return false; +} + +bool QmlBindingCompilerPrivate::parseLogic(QmlJS::AST::Node *node, Result &type) +{ + AST::BinaryExpression *expression = static_cast(node); + + Result lhs; + Result rhs; + + if (!parseExpression(expression->left, lhs)) return false; + if (!parseExpression(expression->right, rhs)) return false; + + type.reg = acquireReg(); + type.metaObject = 0; + type.type = QVariant::Bool; + + if (lhs.type == QMetaType::QReal && rhs.type == QMetaType::QReal) { + + Instr op; + if (expression->op == QSOperator::Gt) + op.common.type = Instr::GreaterThanReal; + else if (expression->op == QSOperator::Equal) + op.common.type = Instr::CompareReal; + else if (expression->op == QSOperator::NotEqual) + op.common.type = Instr::NotCompareReal; + else + return false; + op.binaryop.output = type.reg; + op.binaryop.src1 = lhs.reg; + op.binaryop.src2 = rhs.reg; + bytecode << op; + + + } else if (lhs.type == QMetaType::QString && rhs.type == QMetaType::QString) { + + Instr op; + if (expression->op == QSOperator::Equal) + op.common.type = Instr::CompareString; + else if (expression->op == QSOperator::NotEqual) + op.common.type = Instr::NotCompareString; + else + return false; + op.binaryop.output = type.reg; + op.binaryop.src1 = lhs.reg; + op.binaryop.src2 = rhs.reg; + bytecode << op; + + } else { + return false; + } + + releaseReg(lhs.reg); + releaseReg(rhs.reg); + + return true; +} + +bool QmlBindingCompilerPrivate::tryConditional(QmlJS::AST::Node *node) +{ + return (node->kind == AST::Node::Kind_ConditionalExpression); +} + +bool QmlBindingCompilerPrivate::parseConditional(QmlJS::AST::Node *node, Result &type) +{ + AST::ConditionalExpression *expression = static_cast(node); + + AST::Node *test = expression->expression; + if (test->kind == AST::Node::Kind_NestedExpression) + test = static_cast(test)->expression; + + Result etype; + if (!parseExpression(test, etype)) return false; + + if (etype.type != QVariant::Bool) + return false; + + Instr skip; + skip.common.type = Instr::Skip; + skip.skip.reg = etype.reg; + skip.skip.count = 0; + int skipIdx = bytecode.count(); + bytecode << skip; + + // Release to allow reuse of reg + releaseReg(etype.reg); + + QSet preSubSet = subscriptionSet; + + // int preConditionalSubscriptions = subscriptionSet.count(); + + Result ok; + if (!parseExpression(expression->ok, ok)) return false; + if (ok.unknownType) return false; + + int skipIdx2 = bytecode.count(); + skip.skip.reg = -1; + bytecode << skip; + + // Release to allow reuse of reg + releaseReg(ok.reg); + bytecode[skipIdx].skip.count = bytecode.count() - skipIdx - 1; + + subscriptionSet = preSubSet; + + Result ko; + if (!parseExpression(expression->ko, ko)) return false; + if (ko.unknownType) return false; + + // Release to allow reuse of reg + releaseReg(ko.reg); + bytecode[skipIdx2].skip.count = bytecode.count() - skipIdx2 - 1; + + if (ok != ko) + return false; // Must be same type and in same register + + subscriptionSet = preSubSet; + + if (!subscriptionNeutral(subscriptionSet, ok.subscriptionSet, ko.subscriptionSet)) + return false; // Conditionals cannot introduce new subscriptions + + type = ok; + + return true; +} + +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 || + node->kind == AST::Node::Kind_StringLiteral; +} + +bool QmlBindingCompilerPrivate::parseConstant(QmlJS::AST::Node *node, Result &type) +{ + type.metaObject = 0; + type.type = -1; + type.reg = acquireReg(); + + if (node->kind == AST::Node::Kind_TrueLiteral) { + type.type = QVariant::Bool; + Instr instr; + instr.common.type = Instr::Bool; + instr.bool_value.reg = type.reg; + instr.bool_value.value = true; + bytecode << instr; + return true; + } else if (node->kind == AST::Node::Kind_FalseLiteral) { + type.type = QVariant::Bool; + Instr instr; + instr.common.type = Instr::Bool; + instr.bool_value.reg = type.reg; + instr.bool_value.value = false; + bytecode << instr; + return true; + } else if (node->kind == AST::Node::Kind_NumericLiteral) { + qreal value = qreal(static_cast(node)->value); + + if (qreal(float(value)) != value) + return false; + + type.type = QMetaType::QReal; + Instr instr; + instr.common.type = Instr::Real; + instr.real_value.reg = type.reg; + instr.real_value.value = float(value); + bytecode << instr; + return true; + } else if (node->kind == AST::Node::Kind_StringLiteral) { + QString str = static_cast(node)->value->asString(); + type.type = QMetaType::QString; + type.reg = registerLiteralString(str); + return true; + } else { + return false; + } +} + +bool QmlBindingCompilerPrivate::tryMethod(QmlJS::AST::Node *node) +{ + return node->kind == AST::Node::Kind_CallExpression; +} + +bool QmlBindingCompilerPrivate::parseMethod(QmlJS::AST::Node *node, Result &result) +{ + AST::CallExpression *expr = static_cast(node); + + QStringList name; + if (!buildName(name, expr->base)) + return false; + + if (name.count() != 2 || name.at(0) != QLatin1String("Math")) + return false; + + QString method = name.at(1); + + AST::ArgumentList *args = expr->arguments; + if (!args) return false; + AST::ExpressionNode *arg0 = args->expression; + args = args->next; + if (!args) return false; + AST::ExpressionNode *arg1 = args->expression; + if (args->next != 0) return false; + if (!arg0 || !arg1) return false; + + Result r0; + if (!parseExpression(arg0, r0)) return false; + Result r1; + if (!parseExpression(arg1, r1)) return false; + + if (r0.type != QMetaType::QReal || r1.type != QMetaType::QReal) + return false; + + Instr op; + if (method == QLatin1String("max")) { + op.common.type = Instr::MaxReal; + } else if (method == QLatin1String("min")) { + op.common.type = Instr::MinReal; + } else { + return false; + } + // We release early to reuse registers + releaseReg(r0.reg); + releaseReg(r1.reg); + + op.binaryop.output = acquireReg(); + op.binaryop.src1 = r0.reg; + op.binaryop.src2 = r1.reg; + bytecode << op; + + result.type = QMetaType::QReal; + result.reg = op.binaryop.output; + + return true; +} + +bool QmlBindingCompilerPrivate::buildName(QStringList &name, + QmlJS::AST::Node *node, + QList *nodes) +{ + if (node->kind == AST::Node::Kind_IdentifierExpression) { + name << static_cast(node)->name->asString(); + if (nodes) *nodes << static_cast(node); + } else if (node->kind == AST::Node::Kind_FieldMemberExpression) { + AST::FieldMemberExpression *expr = + static_cast(node); + + if (!buildName(name, expr->base, nodes)) + return false; + + name << expr->name->asString(); + if (nodes) *nodes << expr; + } else { + return false; + } + + return true; +} + + +bool QmlBindingCompilerPrivate::fetch(Result &rv, const QMetaObject *mo, int reg, + int idx, const QStringList &subName, QmlJS::AST::ExpressionNode *node) +{ + QMetaProperty prop = mo->property(idx); + rv.metaObject = 0; + rv.type = 0; + + if (subscription(subName, &rv) && prop.hasNotifySignal() && prop.notifySignalIndex() != -1) { + Instr sub; + sub.common.type = Instr::Subscribe; + sub.subscribe.offset = subscriptionIndex(subName); + sub.subscribe.reg = reg; + sub.subscribe.index = prop.notifySignalIndex(); + bytecode << sub; + } + + Instr fetch; + fetch.common.type = Instr::Fetch; + fetch.fetch.objectReg = reg; + fetch.fetch.index = idx; + fetch.fetch.output = reg; + fetch.fetch.exceptionId = exceptionId(node); + + rv.type = prop.userType(); + rv.metaObject = QmlMetaType::metaObjectForType(rv.type); + rv.reg = reg; + + if (rv.type == QMetaType::QString) { + int tmp = acquireReg(); + Instr copy; + copy.common.type = Instr::Copy; + copy.copy.reg = tmp; + copy.copy.src = reg; + bytecode << copy; + releaseReg(tmp); + fetch.fetch.objectReg = tmp; + + Instr setup; + setup.common.type = Instr::NewString; + setup.construct.reg = reg; + bytecode << setup; + registerCleanup(reg, Instr::CleanupString); + } + + bytecode << fetch; + + if (!rv.metaObject && + rv.type != QMetaType::QReal && + rv.type != QMetaType::Int && + rv.type != QMetaType::Bool && + rv.type != qMetaTypeId() && + rv.type != QMetaType::QString) { + rv.metaObject = 0; + rv.type = 0; + return false; // Unsupported type (string not supported yet); + } + + return true; +} + +void QmlBindingCompilerPrivate::registerCleanup(int reg, int cleanup, int cleanupType) +{ + registerCleanups.insert(reg, qMakePair(cleanup, cleanupType)); +} + +int QmlBindingCompilerPrivate::acquireReg(int cleanup, int cleanupType) +{ + for (int ii = 0; ii < 32; ++ii) { + if (!(registers & (1 << ii))) { + registers |= (1 << ii); + + if (cleanup != Instr::Noop) + registerCleanup(ii, cleanup, cleanupType); + + return ii; + } + } + return -1; +} + +void QmlBindingCompilerPrivate::releaseReg(int reg) +{ + Q_ASSERT(reg >= 0 && reg <= 31); + + if (registerCleanups.contains(reg)) { + QPair c = registerCleanups[reg]; + registerCleanups.remove(reg); + Instr cleanup; + cleanup.common.type = (quint8)c.first; + cleanup.cleanup.reg = reg; + bytecode << cleanup; + } + + quint32 mask = 1 << reg; + registers &= ~mask; +} + +// Returns a reg +int QmlBindingCompilerPrivate::registerLiteralString(const QString &str) +{ + QByteArray strdata((const char *)str.constData(), str.length() * sizeof(QChar)); + int offset = data.count(); + data += strdata; + + int reg = acquireReg(Instr::CleanupString); + + Instr string; + string.common.type = Instr::String; + string.string_value.reg = reg; + string.string_value.offset = offset; + string.string_value.length = str.length(); + bytecode << string; + + return reg; +} + +// Returns an identifier offset +int QmlBindingCompilerPrivate::registerString(const QString &string) +{ + Q_ASSERT(!string.isEmpty()); + + QHash >::ConstIterator iter = registeredStrings.find(string); + + if (iter == registeredStrings.end()) { + quint32 len = string.length(); + QByteArray lendata((const char *)&len, sizeof(quint32)); + QByteArray strdata((const char *)string.constData(), string.length() * sizeof(QChar)); + strdata.prepend(lendata); + int rv = data.count(); + data += strdata; + + iter = registeredStrings.insert(string, qMakePair(registeredStrings.count(), rv)); + } + + Instr reg; + reg.common.type = Instr::InitString; + reg.initstring.offset = iter->first; + reg.initstring.dataIdx = iter->second; + bytecode << reg; + return reg.initstring.offset; +} + +bool QmlBindingCompilerPrivate::subscription(const QStringList &sub, Result *result) +{ + QString str = sub.join(QLatin1String(".")); + result->subscriptionSet.insert(str); + + if (subscriptionSet.contains(str)) { + return false; + } else { + subscriptionSet.insert(str); + return true; + } +} + +int QmlBindingCompilerPrivate::subscriptionIndex(const QStringList &sub) +{ + QString str = sub.join(QLatin1String(".")); + QHash::ConstIterator iter = subscriptionIds.find(str); + if (iter == subscriptionIds.end()) + iter = subscriptionIds.insert(str, subscriptionIds.count()); + usedSubscriptionIds.insert(*iter); + return *iter; +} + +/* + 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 QmlBindingCompilerPrivate::subscriptionNeutral(const QSet &base, + const QSet &lhs, + const QSet &rhs) +{ + QSet difflhs = lhs; + difflhs.subtract(rhs); + QSet diffrhs = rhs; + diffrhs.subtract(lhs); + + difflhs.unite(diffrhs); + difflhs.subtract(base); + + return difflhs.isEmpty(); +} + +quint8 QmlBindingCompilerPrivate::exceptionId(QmlJS::AST::ExpressionNode *n) +{ + quint8 rv = 0xFF; + if (n && exceptions.count() < 0xFF) { + rv = (quint8)exceptions.count(); + QmlJS::AST::SourceLocation l = n->firstSourceLocation(); + quint64 e = l.startLine; + e <<= 32; + e |= l.startColumn; + exceptions.append(e); + } + return rv; +} + +QmlBindingCompiler::QmlBindingCompiler() +: d(new QmlBindingCompilerPrivate) +{ +} + +QmlBindingCompiler::~QmlBindingCompiler() +{ + delete d; d = 0; +} + +/* +Returns true if any bindings were compiled. +*/ +bool QmlBindingCompiler::isValid() const +{ + return !d->committed.bytecode.isEmpty(); +} + +/* +-1 on failure, otherwise the binding index to use. +*/ +int QmlBindingCompiler::compile(const 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 > table; + + for (int ii = 0; ii < committed.count(); ++ii) { + const QSet &deps = committed.dependencies.at(ii); + for (QSet::ConstIterator iter = deps.begin(); iter != deps.end(); ++iter) + table[*iter].append(ii); + } + + QVector header; + QVector data; + for (int ii = 0; ii < committed.subscriptionIds.count(); ++ii) { + header.append(committed.subscriptionIds.count() + data.count()); + const QList &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)); +} + +QByteArray QmlBindingCompilerPrivate::buildExceptionData() const +{ + QByteArray rv; + rv.resize(committed.exceptions.count() * sizeof(quint64)); + ::memcpy(rv.data(), committed.exceptions.constData(), rv.size()); + return rv; +} + +/* +Returns the compiled program. +*/ +QByteArray QmlBindingCompiler::program() const +{ + QByteArray programData; + + if (isValid()) { + Program prog; + prog.bindings = d->committed.count(); + + QVector bytecode; + Instr skip; + skip.common.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(); + while (data.count() % 4) data.append('\0'); + prog.exceptionDataOffset = data.count(); + data += d->buildExceptionData(); + + prog.dataLength = 4 * ((data.size() + 3) / 4); + prog.subscriptions = d->committed.subscriptionIds.count(); + prog.identifiers = d->committed.registeredStrings.count(); + prog.instructionCount = bytecode.count(); + 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/qmlcompiledbindings_p.h b/src/declarative/qml/qmlcompiledbindings_p.h new file mode 100644 index 0000000..1d8fac4 --- /dev/null +++ b/src/declarative/qml/qmlcompiledbindings_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLBINDINGOPTIMIZATIONS_P_H +#define QMLBINDINGOPTIMIZATIONS_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 "qmlexpression_p.h" +#include "qmlbinding.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QmlBindingCompilerPrivate; +class QmlBindingCompiler +{ +public: + QmlBindingCompiler(); + ~QmlBindingCompiler(); + + // Returns true if bindings were compiled + bool isValid() const; + + struct Expression + { + QmlParser::Object *component; + QmlParser::Object *context; + QmlParser::Property *property; + QmlParser::Variant expression; + QHash ids; + QmlEnginePrivate::Imports imports; + }; + + // -1 on failure, otherwise the binding index to use + int compile(const Expression &, QmlEnginePrivate *); + + // Returns the compiled program + QByteArray program() const; + + static void dump(const QByteArray &); +private: + QmlBindingCompilerPrivate *d; +}; + +class QmlCompiledBindingsPrivate; +class QmlCompiledBindings : public QObject, public QmlAbstractExpression, public QmlRefCount +{ +public: + QmlCompiledBindings(const char *program, QmlContext *context); + virtual ~QmlCompiledBindings(); + + QmlAbstractBinding *configBinding(int index, QObject *target, QObject *scope, int property); + +protected: + int qt_metacall(QMetaObject::Call, int, void **); + +private: + Q_DISABLE_COPY(QmlCompiledBindings); + Q_DECLARE_PRIVATE(QmlCompiledBindings); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QMLBINDINGOPTIMIZATIONS_P_H + diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index 5937734..2e18b85 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -64,7 +64,7 @@ #include "qmlglobal_p.h" #include "qmlscriptparser_p.h" #include "qmlbinding.h" -#include "qmlbindingvme_p.h" +#include "qmlcompiledbindings_p.h" #include @@ -2672,7 +2672,7 @@ bool QmlCompiler::completeComponentBuild() if (bindingCompiler.isValid()) { compileState.compiledBindingData = bindingCompiler.program(); - QmlBindingVME::dump(compileState.compiledBindingData); + QmlBindingCompiler::dump(compileState.compiledBindingData); } saveComponentState(); diff --git a/src/declarative/qml/qmlcontext.cpp b/src/declarative/qml/qmlcontext.cpp index f97f142..0eb497d 100644 --- a/src/declarative/qml/qmlcontext.cpp +++ b/src/declarative/qml/qmlcontext.cpp @@ -45,7 +45,7 @@ #include "qmlexpression_p.h" #include "qmlengine_p.h" #include "qmlengine.h" -#include "qmlbindingoptimizations_p.h" +#include "qmlcompiledbindings_p.h" #include "qmlinfo.h" #include @@ -108,7 +108,7 @@ void QmlContextPrivate::destroyed(ContextGuard *guard) while(guard->bindings) { QObject *o = guard->bindings->target; int mi = guard->bindings->methodIndex; - guard->bindings->reset(); + guard->bindings->clear(); if (o) o->qt_metacall(QMetaObject::InvokeMetaMethod, mi, 0); } diff --git a/src/declarative/qml/qmlcontext_p.h b/src/declarative/qml/qmlcontext_p.h index ffb4298..cd7e1b6 100644 --- a/src/declarative/qml/qmlcontext_p.h +++ b/src/declarative/qml/qmlcontext_p.h @@ -59,7 +59,6 @@ #include "qmlengine_p.h" #include "qmlintegercache_p.h" #include "qmltypenamecache_p.h" -#include "qmlbindingvme_p.h" #include #include @@ -77,7 +76,7 @@ class QmlExpression; class QmlExpressionPrivate; class QmlAbstractExpression; class QmlBinding_Id; -class QmlOptimizedBindings; +class QmlCompiledBindings; class Q_DECLARATIVE_EXPORT QmlContextPrivate : public QObjectPrivate { @@ -114,15 +113,27 @@ public: QmlDeclarativeData *contextObjects; + struct IdNotifier + { + inline IdNotifier(); + inline ~IdNotifier(); + + inline void clear(); + + IdNotifier *next; + IdNotifier**prev; + QObject *target; + int methodIndex; + }; + struct ContextGuard : public QmlGuard { - ContextGuard() : priv(0), bindings(0) {} + inline ContextGuard(); + inline ContextGuard &operator=(QObject *obj); + inline virtual void objectDestroyed(QObject *); + QmlContextPrivate *priv; - QmlBindingVME::Config::Subscription::Id *bindings; - ContextGuard &operator=(QObject *obj) { - (QmlGuard&)*this = obj; return *this; - } - void objectDestroyed(QObject *) { priv->destroyed(this); } + IdNotifier *bindings; }; ContextGuard *idValues; int idValueCount; @@ -137,12 +148,45 @@ public: return static_cast(context->q_func()); } - QmlOptimizedBindings *optimizedBindings; + QmlCompiledBindings *optimizedBindings; // Only used for debugging QList > instances; }; +QmlContextPrivate::IdNotifier::IdNotifier() +: next(0), prev(0), target(0), methodIndex(-1) +{ +} + +QmlContextPrivate::IdNotifier::~IdNotifier() +{ + clear(); +} + +void QmlContextPrivate::IdNotifier::clear() +{ + if (next) next->prev = prev; + if (prev) *prev = next; + next = 0; prev = 0; target = 0; + methodIndex = -1; +} + +QmlContextPrivate::ContextGuard::ContextGuard() +: priv(0), bindings(0) +{ +} + +QmlContextPrivate::ContextGuard &QmlContextPrivate::ContextGuard::operator=(QObject *obj) +{ + (QmlGuard&)*this = obj; return *this; +} + +void QmlContextPrivate::ContextGuard::objectDestroyed(QObject *) +{ + priv->destroyed(this); +} + QT_END_NAMESPACE #endif // QMLCONTEXT_P_H diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index 6b2e7af..e9a0449 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -57,7 +57,7 @@ #include "qmlvmemetaobject_p.h" #include "qmlbinding_p.h" #include "qmlcontext_p.h" -#include "qmlbindingoptimizations_p.h" +#include "qmlcompiledbindings_p.h" #include "qmlglobal_p.h" #include "qmlscriptstring.h" @@ -173,7 +173,7 @@ QObject *QmlVME::run(QmlVMEStack &stack, QmlContext *ctxt, if (instr.init.contextCache != -1) cp->setIdPropertyData(comp->contextCaches.at(instr.init.contextCache)); if (instr.init.compiledBinding != -1) - cp->optimizedBindings = new QmlOptimizedBindings(datas.at(instr.init.compiledBinding).constData(), ctxt); + cp->optimizedBindings = new QmlCompiledBindings(datas.at(instr.init.compiledBinding).constData(), ctxt); } break; -- cgit v0.12