aboutsummaryrefslogtreecommitdiffstats
path: root/src/declarative/qml/qdeclarativecompiledbindings.cpp
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit885735d011472bcfbb96e688d9e64553d7fe9d4b (patch)
tree734963625eba643bf11bc4870a4c407809a6400a /src/declarative/qml/qdeclarativecompiledbindings.cpp
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/declarative/qml/qdeclarativecompiledbindings.cpp')
-rw-r--r--src/declarative/qml/qdeclarativecompiledbindings.cpp2906
1 files changed, 2906 insertions, 0 deletions
diff --git a/src/declarative/qml/qdeclarativecompiledbindings.cpp b/src/declarative/qml/qdeclarativecompiledbindings.cpp
new file mode 100644
index 0000000000..a6fcce4c99
--- /dev/null
+++ b/src/declarative/qml/qdeclarativecompiledbindings.cpp
@@ -0,0 +1,2906 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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$
+**
+****************************************************************************/
+
+// #define COMPILEDBINDINGS_DEBUG
+// #define REGISTER_CLEANUP_DEBUG
+
+#include "private/qdeclarativecompiledbindings_p.h"
+
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <private/qdeclarativecontext_p.h>
+#include <private/qdeclarativejsast_p.h>
+#include <private/qdeclarativejsengine_p.h>
+#include <private/qdeclarativeexpression_p.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qnumeric.h>
+#include <private/qdeclarativeanchors_p_p.h>
+#include <private/qdeclarativeglobal_p.h>
+#include <private/qdeclarativefastproperties_p.h>
+#include <private/qdeclarativedebugtrace_p.h>
+
+QT_BEGIN_NAMESPACE
+
+DEFINE_BOOL_CONFIG_OPTION(qmlExperimental, QML_EXPERIMENTAL);
+DEFINE_BOOL_CONFIG_OPTION(qmlDisableOptimizer, QML_DISABLE_OPTIMIZER);
+DEFINE_BOOL_CONFIG_OPTION(qmlDisableFastProperties, QML_DISABLE_FAST_PROPERTIES);
+DEFINE_BOOL_CONFIG_OPTION(bindingsDump, QML_BINDINGS_DUMP);
+
+Q_GLOBAL_STATIC(QDeclarativeFastProperties, fastProperties);
+
+#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200)
+# define QML_THREADED_INTERPRETER
+#endif
+
+#define FOR_EACH_QML_INSTR(F) \
+ F(Noop) /* Nop */ \
+ F(BindingId) /* id */ \
+ F(Subscribe) /* subscribe */ \
+ F(SubscribeId) /* subscribe */ \
+ F(FetchAndSubscribe) /* fetchAndSubscribe */ \
+ F(LoadId) /* load */ \
+ F(LoadScope) /* load */ \
+ F(LoadRoot) /* load */ \
+ F(LoadAttached) /* attached */ \
+ F(ConvertIntToReal) /* unaryop */ \
+ F(ConvertRealToInt) /* unaryop */ \
+ F(Real) /* real_value */ \
+ F(Int) /* int_value */ \
+ F(Bool) /* bool_value */ \
+ F(String) /* string_value */ \
+ F(AddReal) /* binaryop */ \
+ F(AddInt) /* binaryop */ \
+ F(AddString) /* binaryop */ \
+ F(MinusReal) /* binaryop */ \
+ F(MinusInt) /* binaryop */ \
+ F(CompareReal) /* binaryop */ \
+ F(CompareString) /* binaryop */ \
+ F(NotCompareReal) /* binaryop */ \
+ F(NotCompareString) /* binaryop */ \
+ F(GreaterThanReal) /* binaryop */ \
+ F(MaxReal) /* binaryop */ \
+ F(MinReal) /* binaryop */ \
+ F(NewString) /* construct */ \
+ F(NewUrl) /* construct */ \
+ F(CleanupUrl) /* cleanup */ \
+ F(CleanupString) /* cleanup */ \
+ F(Copy) /* copy */ \
+ F(Fetch) /* fetch */ \
+ F(Store) /* store */ \
+ F(Skip) /* skip */ \
+ F(Done) /* done */ \
+ /* Speculative property resolution */ \
+ F(InitString) /* initstring */ \
+ F(FindGeneric) /* find */ \
+ F(FindGenericTerminal) /* find */ \
+ F(FindProperty) /* find */ \
+ F(FindPropertyTerminal) /* find */ \
+ F(CleanupGeneric) /* cleanup */ \
+ F(ConvertGenericToReal) /* unaryop */ \
+ F(ConvertGenericToBool) /* unaryop */ \
+ F(ConvertGenericToString) /* unaryop */ \
+ F(ConvertGenericToUrl) /* unaryop */
+
+#define QML_INSTR_ENUM(I) I,
+#define QML_INSTR_ADDR(I) &&op_##I,
+
+#ifdef QML_THREADED_INTERPRETER
+# define QML_BEGIN_INSTR(I) op_##I:
+# define QML_END_INSTR(I) ++instr; goto *instr->common.code;
+# define QML_INSTR_HEADER void *code;
+#else
+# define QML_BEGIN_INSTR(I) case Instr::I:
+# define QML_END_INSTR(I) break;
+# define QML_INSTR_HEADER
+#endif
+
+
+using namespace QDeclarativeJS;
+
+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
+
+#ifdef REGISTER_CLEANUP_DEBUG
+ Register() {
+ type = 0;
+ }
+
+ ~Register() {
+ int allowedTypes[] = { QMetaType::QObjectStar, QMetaType::QReal, QMetaType::Int, QMetaType::Bool, 0 };
+ bool found = (type == 0);
+ int *ctype = allowedTypes;
+ while (!found && *ctype) {
+ found = (*ctype == type);
+ ++ctype;
+ }
+ if (!found)
+ qWarning("Register leaked of type %d", type);
+ }
+#endif
+};
+}
+
+class QDeclarativeCompiledBindingsPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QDeclarativeCompiledBindings)
+
+public:
+ QDeclarativeCompiledBindingsPrivate();
+ virtual ~QDeclarativeCompiledBindingsPrivate();
+
+ struct Binding : public QDeclarativeAbstractBinding, public QDeclarativeDelayedError {
+ Binding() : enabled(false), updating(0), property(0),
+ scope(0), target(0), parent(0) {}
+
+ // Inherited from QDeclarativeAbstractBinding
+ virtual void setEnabled(bool, QDeclarativePropertyPrivate::WriteFlags flags);
+ virtual void update(QDeclarativePropertyPrivate::WriteFlags flags);
+ virtual void destroy();
+
+ int index:30;
+ bool enabled:1;
+ bool updating:1;
+ int property;
+ QObject *scope;
+ QObject *target;
+
+ QDeclarativeCompiledBindingsPrivate *parent;
+ };
+
+ typedef QDeclarativeNotifierEndpoint Subscription;
+ Subscription *subscriptions;
+ QScriptDeclarativeClass::PersistentIdentifier *identifiers;
+
+ void run(Binding *, QDeclarativePropertyPrivate::WriteFlags flags);
+
+ const char *programData;
+ Binding *m_bindings;
+ quint32 *m_signalTable;
+
+ static int methodCount;
+
+ void init();
+ void run(int instr, QDeclarativeContextData *context,
+ QDeclarativeDelayedError *error, QObject *scope, QObject *output, QDeclarativePropertyPrivate::WriteFlags storeFlags);
+
+
+ inline void unsubscribe(int subIndex);
+ inline void subscribeId(QDeclarativeContextData *p, int idIndex, int subIndex);
+ inline void subscribe(QObject *o, int notifyIndex, int subIndex);
+
+ QDeclarativePropertyCache::Data *findproperty(QObject *obj,
+ const QScriptDeclarativeClass::Identifier &name,
+ QDeclarativeEnginePrivate *enginePriv,
+ QDeclarativePropertyCache::Data &local);
+ bool findproperty(QObject *obj,
+ Register *output,
+ QDeclarativeEnginePrivate *enginePriv,
+ int subIdx,
+ const QScriptDeclarativeClass::Identifier &name,
+ bool isTerminal);
+ void findgeneric(Register *output, // value output
+ int subIdx, // Subscription index in config
+ QDeclarativeContextData *context, // Context to search in
+ const QScriptDeclarativeClass::Identifier &name,
+ bool isTerminal);
+};
+
+QDeclarativeCompiledBindingsPrivate::QDeclarativeCompiledBindingsPrivate()
+: subscriptions(0), identifiers(0)
+{
+}
+
+QDeclarativeCompiledBindingsPrivate::~QDeclarativeCompiledBindingsPrivate()
+{
+ delete [] subscriptions; subscriptions = 0;
+ delete [] identifiers; identifiers = 0;
+}
+
+int QDeclarativeCompiledBindingsPrivate::methodCount = -1;
+
+QDeclarativeCompiledBindings::QDeclarativeCompiledBindings(const char *program, QDeclarativeContextData *context)
+: QObject(*(new QDeclarativeCompiledBindingsPrivate))
+{
+ Q_D(QDeclarativeCompiledBindings);
+
+ if (d->methodCount == -1)
+ d->methodCount = QDeclarativeCompiledBindings::staticMetaObject.methodCount();
+
+ d->programData = program;
+
+ d->init();
+
+ QDeclarativeAbstractExpression::setContext(context);
+}
+
+QDeclarativeCompiledBindings::~QDeclarativeCompiledBindings()
+{
+ Q_D(QDeclarativeCompiledBindings);
+
+ delete [] d->m_bindings;
+}
+
+QDeclarativeAbstractBinding *QDeclarativeCompiledBindings::configBinding(int index, QObject *target,
+ QObject *scope, int property)
+{
+ Q_D(QDeclarativeCompiledBindings);
+
+ QDeclarativeCompiledBindingsPrivate::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 QDeclarativeCompiledBindingsPrivate::Binding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags)
+{
+ if (enabled != e) {
+ enabled = e;
+
+ if (e) update(flags);
+ }
+}
+
+void QDeclarativeCompiledBindingsPrivate::Binding::update(QDeclarativePropertyPrivate::WriteFlags flags)
+{
+ QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Binding);
+ parent->run(this, flags);
+ QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Binding);
+}
+
+void QDeclarativeCompiledBindingsPrivate::Binding::destroy()
+{
+ enabled = false;
+ removeFromObject();
+ clear();
+ parent->q_func()->release();
+}
+
+int QDeclarativeCompiledBindings::qt_metacall(QMetaObject::Call c, int id, void **)
+{
+ Q_D(QDeclarativeCompiledBindings);
+
+ 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], QDeclarativePropertyPrivate::DontRemoveBinding);
+ }
+ }
+ return -1;
+}
+
+void QDeclarativeCompiledBindingsPrivate::run(Binding *binding, QDeclarativePropertyPrivate::WriteFlags flags)
+{
+ Q_Q(QDeclarativeCompiledBindings);
+
+ if (!binding->enabled)
+ return;
+
+ QDeclarativeContextData *context = q->QDeclarativeAbstractExpression::context();
+ if (!context || !context->isValid())
+ return;
+
+ if (binding->updating) {
+ QString name;
+ if (binding->property & 0xFFFF0000) {
+ QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context->engine);
+
+ QDeclarativeValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF];
+ Q_ASSERT(vt);
+
+ name = QLatin1String(binding->target->metaObject()->property(binding->property & 0xFFFF).name());
+ name.append(QLatin1String("."));
+ name.append(QLatin1String(vt->metaObject()->property(binding->property >> 24).name()));
+ } else {
+ name = QLatin1String(binding->target->metaObject()->property(binding->property).name());
+ }
+ qmlInfo(binding->target) << QCoreApplication::translate("QDeclarativeCompiledBindings", "Binding loop detected for property \"%1\"").arg(name);
+ return;
+ }
+
+ binding->updating = true;
+ if (binding->property & 0xFFFF0000) {
+ QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context->engine);
+
+ QDeclarativeValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF];
+ Q_ASSERT(vt);
+ vt->read(binding->target, binding->property & 0xFFFF);
+
+ QObject *target = vt;
+ run(binding->index, context, binding, binding->scope, target, flags);
+
+ vt->write(binding->target, binding->property & 0xFFFF, flags);
+ } else {
+ run(binding->index, context, binding, binding->scope, binding->target, flags);
+ }
+ binding->updating = false;
+}
+
+namespace {
+// This structure is exactly 8-bytes in size
+struct Instr {
+ enum {
+ FOR_EACH_QML_INSTR(QML_INSTR_ENUM)
+ };
+
+ union {
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ quint8 packing[7];
+ } common;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ quint8 packing;
+ quint16 column;
+ quint32 line;
+ } id;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ quint8 packing[3];
+ quint16 subscriptions;
+ quint16 identifiers;
+ } init;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 reg;
+ quint16 offset;
+ quint32 index;
+ } subscribe;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 reg;
+ quint8 packing[2];
+ quint32 index;
+ } load;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 output;
+ qint8 reg;
+ quint8 exceptionId;
+ quint32 id;
+ } attached;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 output;
+ qint8 reg;
+ quint8 exceptionId;
+ quint32 index;
+ } store;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 output;
+ qint8 objectReg;
+ quint8 exceptionId;
+ quint16 subscription;
+ quint16 function;
+ } fetchAndSubscribe;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 output;
+ qint8 objectReg;
+ quint8 exceptionId;
+ quint32 index;
+ } fetch;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 reg;
+ qint8 src;
+ quint8 packing[5];
+ } copy;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 reg;
+ quint8 packing[6];
+ } construct;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 reg;
+ quint8 packing[2];
+ float value;
+ } real_value;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 reg;
+ quint8 packing[2];
+ int value;
+ } int_value;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 reg;
+ bool value;
+ quint8 packing[5];
+ } bool_value;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 reg;
+ quint16 length;
+ quint32 offset;
+ } string_value;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 output;
+ qint8 src1;
+ qint8 src2;
+ quint8 packing[4];
+ } binaryop;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 output;
+ qint8 src;
+ quint8 packing[5];
+ } unaryop;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 reg;
+ quint8 packing[2];
+ quint32 count;
+ } skip;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 reg;
+ qint8 src;
+ quint8 exceptionId;
+ quint16 name;
+ quint16 subscribeIndex;
+ } find;
+ struct {
+ QML_INSTR_HEADER
+ quint8 type;
+ qint8 reg;
+ quint8 packing[6];
+ } cleanup;
+ struct {
+ QML_INSTR_HEADER
+ 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 compiled;
+
+ const char *data() const { return ((const char *)this) + sizeof(Program); }
+ const Instr *instructions() const { return (const Instr *)(data() + dataLength); }
+};
+}
+
+struct QDeclarativeBindingCompilerPrivate
+{
+ 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<QString> subscriptionSet;
+ };
+
+ QDeclarativeBindingCompilerPrivate() : registers(0) {}
+
+ void resetInstanceState();
+ int commitCompile();
+
+ QDeclarativeParser::Object *context;
+ QDeclarativeParser::Object *component;
+ QDeclarativeParser::Property *destination;
+ QHash<QString, QDeclarativeParser::Object *> ids;
+ QDeclarativeImports imports;
+ QDeclarativeEnginePrivate *engine;
+
+ QString contextName() const { return QLatin1String("$$$SCOPE_") + QString::number((quintptr)context, 16); }
+
+ bool compile(QDeclarativeJS::AST::Node *);
+
+ bool parseExpression(QDeclarativeJS::AST::Node *, Result &);
+
+ bool tryName(QDeclarativeJS::AST::Node *);
+ bool parseName(QDeclarativeJS::AST::Node *, Result &);
+
+ bool tryArith(QDeclarativeJS::AST::Node *);
+ bool parseArith(QDeclarativeJS::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(QDeclarativeJS::AST::Node *);
+ bool parseLogic(QDeclarativeJS::AST::Node *, Result &);
+
+ bool tryConditional(QDeclarativeJS::AST::Node *);
+ bool parseConditional(QDeclarativeJS::AST::Node *, Result &);
+
+ bool tryConstant(QDeclarativeJS::AST::Node *);
+ bool parseConstant(QDeclarativeJS::AST::Node *, Result &);
+
+ bool tryMethod(QDeclarativeJS::AST::Node *);
+ bool parseMethod(QDeclarativeJS::AST::Node *, Result &);
+
+ bool buildName(QStringList &, QDeclarativeJS::AST::Node *, QList<QDeclarativeJS::AST::ExpressionNode *> *nodes = 0);
+ bool fetch(Result &type, const QMetaObject *, int reg, int idx, const QStringList &, QDeclarativeJS::AST::ExpressionNode *);
+
+ quint32 registers;
+ QHash<int, QPair<int, int> > 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<QString, QPair<int, int> > registeredStrings;
+ QByteArray data;
+
+ bool subscription(const QStringList &, Result *);
+ int subscriptionIndex(const QStringList &);
+ bool subscriptionNeutral(const QSet<QString> &base, const QSet<QString> &lhs, const QSet<QString> &rhs);
+
+ quint8 exceptionId(QDeclarativeJS::AST::ExpressionNode *);
+ QVector<quint64> exceptions;
+
+ QSet<int> usedSubscriptionIds;
+ QSet<QString> subscriptionSet;
+ QHash<QString, int> subscriptionIds;
+ QVector<Instr> bytecode;
+
+ // Committed binding data
+ struct {
+ QList<int> offsets;
+ QList<QSet<int> > dependencies;
+
+ QVector<Instr> bytecode;
+ QByteArray data;
+ QHash<QString, int> subscriptionIds;
+ QVector<quint64> exceptions;
+
+ QHash<QString, QPair<int, int> > registeredStrings;
+
+ int count() const { return offsets.count(); }
+ } committed;
+
+ QByteArray buildSignalTable() const;
+ QByteArray buildExceptionData() const;
+};
+
+void QDeclarativeCompiledBindingsPrivate::unsubscribe(int subIndex)
+{
+ QDeclarativeCompiledBindingsPrivate::Subscription *sub = (subscriptions + subIndex);
+ sub->disconnect();
+}
+
+void QDeclarativeCompiledBindingsPrivate::subscribeId(QDeclarativeContextData *p, int idIndex, int subIndex)
+{
+ Q_Q(QDeclarativeCompiledBindings);
+
+ unsubscribe(subIndex);
+
+ if (p->idValues[idIndex]) {
+ QDeclarativeCompiledBindingsPrivate::Subscription *sub = (subscriptions + subIndex);
+ sub->target = q;
+ sub->targetMethod = methodCount + subIndex;
+ sub->connect(&p->idValues[idIndex].bindings);
+ }
+}
+
+void QDeclarativeCompiledBindingsPrivate::subscribe(QObject *o, int notifyIndex, int subIndex)
+{
+ Q_Q(QDeclarativeCompiledBindings);
+
+ QDeclarativeCompiledBindingsPrivate::Subscription *sub = (subscriptions + subIndex);
+ sub->target = q;
+ sub->targetMethod = methodCount + subIndex;
+ if (o)
+ sub->connect(o, notifyIndex);
+ else
+ sub->disconnect();
+}
+
+// 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<QVariant>()) {
+ 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<QVariant>()) {
+ 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<QVariant>()) {
+ return reg->getvariantptr()->toBool();
+ } else {
+ if (ok) *ok = false;
+ return false;
+ }
+}
+
+inline static QUrl toUrl(Register *reg, int type, QDeclarativeContextData *context, bool *ok = 0)
+{
+ if (ok) *ok = true;
+
+ QUrl base;
+ if (type == qMetaTypeId<QVariant>()) {
+ 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<QObject*>(value);
+ } else {
+ if (ok) *ok = false;
+ return 0;
+ }
+}
+
+bool QDeclarativeCompiledBindingsPrivate::findproperty(QObject *obj, Register *output,
+ QDeclarativeEnginePrivate *enginePriv,
+ int subIdx, const QScriptDeclarativeClass::Identifier &name,
+ bool isTerminal)
+{
+ if (!obj) {
+ output->setUndefined();
+ return false;
+ }
+
+ QDeclarativePropertyCache::Data local;
+ QDeclarativePropertyCache::Data *property =
+ QDeclarativePropertyCache::property(QDeclarativeEnginePrivate::get(enginePriv), obj, name, local);
+
+ if (property) {
+ if (subIdx != -1)
+ subscribe(obj, property->notifyIndex, subIdx);
+
+ if (property->flags & QDeclarativePropertyCache::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>()) {
+ QVariant v;
+ void *args[] = { &v, 0 };
+ QMetaObject::metacall(obj, QMetaObject::ReadProperty, property->coreIndex, args);
+
+ if (isTerminal) {
+ new (output->typeDataPtr()) QVariant(v);
+ output->settype(qMetaTypeId<QVariant>());
+ } 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<QVariant>());
+ }
+ }
+
+ return true;
+ } else {
+ output->setUndefined();
+ return false;
+ }
+}
+
+void QDeclarativeCompiledBindingsPrivate::findgeneric(Register *output,
+ int subIdx,
+ QDeclarativeContextData *context,
+ const QScriptDeclarativeClass::Identifier &name,
+ bool isTerminal)
+{
+ QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(context->engine);
+
+ while (context) {
+
+ int contextPropertyIndex = context->propertyNames?context->propertyNames->value(name):-1;
+
+
+ if (contextPropertyIndex != -1) {
+
+ if (contextPropertyIndex < context->idValueCount) {
+ output->setQObject(context->idValues[contextPropertyIndex]);
+ output->settype(QMetaType::QObjectStar);
+
+ if (subIdx != -1)
+ subscribeId(context, contextPropertyIndex, subIdx);
+
+ } else {
+ QDeclarativeContextPrivate *cp = context->asQDeclarativeContextPrivate();
+ const QVariant &value = cp->propertyValues.at(contextPropertyIndex);
+
+ if (isTerminal) {
+ new (output->typeDataPtr()) QVariant(value);
+ output->settype(qMetaTypeId<QVariant>());
+ } else {
+ bool ok;
+ output->setQObject(variantToQObject(value, &ok));
+ if (!ok) { output->setUndefined(); }
+ else { output->settype(QMetaType::QObjectStar); }
+ return;
+ }
+
+ if (subIdx != -1)
+ subscribe(context->asQDeclarativeContext(), contextPropertyIndex + cp->notifyIndex, subIdx);
+
+
+ }
+
+ return;
+ }
+
+ if (QObject *root = context->contextObject) {
+
+ if (findproperty(root, output, enginePriv, subIdx, name, isTerminal))
+ return;
+
+ }
+
+ context = context->parent;
+ }
+
+ output->setUndefined();
+}
+
+void QDeclarativeCompiledBindingsPrivate::init()
+{
+ Program *program = (Program *)programData;
+ if (program->subscriptions)
+ subscriptions = new QDeclarativeCompiledBindingsPrivate::Subscription[program->subscriptions];
+ if (program->identifiers)
+ identifiers = new QScriptDeclarativeClass::PersistentIdentifier[program->identifiers];
+
+ m_signalTable = (quint32 *)(program->data() + program->signalTableOffset);
+ m_bindings = new QDeclarativeCompiledBindingsPrivate::Binding[program->bindings];
+}
+
+static void throwException(int id, QDeclarativeDelayedError *error,
+ Program *program, QDeclarativeContextData *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(QDeclarativeEnginePrivate::get(context->engine)))
+ QDeclarativeEnginePrivate::warning(context->engine, error->error);
+}
+
+static void dumpInstruction(const Instr *instr)
+{
+ switch (instr->common.type) {
+ case Instr::Noop:
+ qWarning().nospace() << "\t" << "Noop";
+ break;
+ case Instr::BindingId:
+ qWarning().nospace() << instr->id.line << ":" << instr->id.column << ":";
+ break;
+ case Instr::Subscribe:
+ qWarning().nospace() << "\t" << "Subscribe" << "\t\t" << instr->subscribe.offset << "\t" << instr->subscribe.reg << "\t" << instr->subscribe.index;
+ break;
+ case Instr::SubscribeId:
+ qWarning().nospace() << "\t" << "SubscribeId" << "\t\t" << instr->subscribe.offset << "\t" << instr->subscribe.reg << "\t" << instr->subscribe.index;
+ break;
+ case Instr::FetchAndSubscribe:
+ qWarning().nospace() << "\t" << "FetchAndSubscribe" << "\t" << instr->fetchAndSubscribe.output << "\t" << instr->fetchAndSubscribe.objectReg << "\t" << instr->fetchAndSubscribe.subscription;
+ break;
+ case Instr::LoadId:
+ qWarning().nospace() << "\t" << "LoadId" << "\t\t\t" << instr->load.index << "\t" << instr->load.reg;
+ break;
+ case Instr::LoadScope:
+ qWarning().nospace() << "\t" << "LoadScope" << "\t\t" << instr->load.index << "\t" << instr->load.reg;
+ break;
+ case Instr::LoadRoot:
+ qWarning().nospace() << "\t" << "LoadRoot" << "\t\t" << instr->load.index << "\t" << instr->load.reg;
+ break;
+ case Instr::LoadAttached:
+ qWarning().nospace() << "\t" << "LoadAttached" << "\t\t" << instr->attached.output << "\t" << instr->attached.reg << "\t" << instr->attached.id;
+ break;
+ case Instr::ConvertIntToReal:
+ qWarning().nospace() << "\t" << "ConvertIntToReal" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src;
+ break;
+ case Instr::ConvertRealToInt:
+ qWarning().nospace() << "\t" << "ConvertRealToInt" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src;
+ break;
+ case Instr::Real:
+ qWarning().nospace() << "\t" << "Real" << "\t\t\t" << instr->real_value.reg << "\t" << instr->real_value.value;
+ break;
+ case Instr::Int:
+ qWarning().nospace() << "\t" << "Int" << "\t\t\t" << instr->int_value.reg << "\t" << instr->int_value.value;
+ break;
+ case Instr::Bool:
+ qWarning().nospace() << "\t" << "Bool" << "\t\t\t" << instr->bool_value.reg << "\t" << instr->bool_value.value;
+ break;
+ case Instr::String:
+ qWarning().nospace() << "\t" << "String" << "\t\t\t" << instr->string_value.reg << "\t" << instr->string_value.offset << "\t" << instr->string_value.length;
+ break;
+ case Instr::AddReal:
+ qWarning().nospace() << "\t" << "AddReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2;
+ break;
+ case Instr::AddInt:
+ qWarning().nospace() << "\t" << "AddInt" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2;
+ break;
+ case Instr::AddString:
+ qWarning().nospace() << "\t" << "AddString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2;
+ break;
+ case Instr::MinusReal:
+ qWarning().nospace() << "\t" << "MinusReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2;
+ break;
+ case Instr::MinusInt:
+ qWarning().nospace() << "\t" << "MinusInt" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2;
+ break;
+ case Instr::CompareReal:
+ qWarning().nospace() << "\t" << "CompareReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2;
+ break;
+ case Instr::CompareString:
+ qWarning().nospace() << "\t" << "CompareString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2;
+ break;
+ case Instr::NotCompareReal:
+ qWarning().nospace() << "\t" << "NotCompareReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2;
+ break;
+ case Instr::NotCompareString:
+ qWarning().nospace() << "\t" << "NotCompareString" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2;
+ break;
+ case Instr::GreaterThanReal:
+ qWarning().nospace() << "\t" << "GreaterThanReal" << "\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2;
+ break;
+ case Instr::MaxReal:
+ qWarning().nospace() << "\t" << "MaxReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2;
+ break;
+ case Instr::MinReal:
+ qWarning().nospace() << "\t" << "MinReal" << "\t\t\t" << instr->binaryop.output << "\t" << instr->binaryop.src1 << "\t" << instr->binaryop.src2;
+ break;
+ case Instr::NewString:
+ qWarning().nospace() << "\t" << "NewString" << "\t\t" << instr->construct.reg;
+ break;
+ case Instr::NewUrl:
+ qWarning().nospace() << "\t" << "NewUrl" << "\t\t\t" << instr->construct.reg;
+ break;
+ case Instr::CleanupString:
+ qWarning().nospace() << "\t" << "CleanupString" << "\t\t" << instr->cleanup.reg;
+ break;
+ case Instr::CleanupUrl:
+ qWarning().nospace() << "\t" << "CleanupUrl" << "\t\t" << instr->cleanup.reg;
+ break;
+ case Instr::Fetch:
+ qWarning().nospace() << "\t" << "Fetch" << "\t\t\t" << instr->fetch.output << "\t" << instr->fetch.index << "\t" << instr->fetch.objectReg;
+ break;
+ case Instr::Store:
+ qWarning().nospace() << "\t" << "Store" << "\t\t\t" << instr->store.output << "\t" << instr->store.index << "\t" << instr->store.reg;
+ break;
+ case Instr::Copy:
+ qWarning().nospace() << "\t" << "Copy" << "\t\t\t" << instr->copy.reg << "\t" << instr->copy.src;
+ break;
+ case Instr::Skip:
+ qWarning().nospace() << "\t" << "Skip" << "\t\t\t" << instr->skip.reg << "\t" << instr->skip.count;
+ break;
+ case Instr::Done:
+ qWarning().nospace() << "\t" << "Done";
+ break;
+ case Instr::InitString:
+ qWarning().nospace() << "\t" << "InitString" << "\t\t" << instr->initstring.offset << "\t" << instr->initstring.dataIdx;
+ break;
+ case Instr::FindGeneric:
+ qWarning().nospace() << "\t" << "FindGeneric" << "\t\t" << instr->find.reg << "\t" << instr->find.name;
+ break;
+ case Instr::FindGenericTerminal:
+ qWarning().nospace() << "\t" << "FindGenericTerminal" << "\t" << instr->find.reg << "\t" << instr->find.name;
+ break;
+ case Instr::FindProperty:
+ qWarning().nospace() << "\t" << "FindProperty" << "\t\t" << instr->find.reg << "\t" << instr->find.src << "\t" << instr->find.name;
+ break;
+ case Instr::FindPropertyTerminal:
+ qWarning().nospace() << "\t" << "FindPropertyTerminal" << "\t" << instr->find.reg << "\t" << instr->find.src << "\t" << instr->find.name;
+ break;
+ case Instr::CleanupGeneric:
+ qWarning().nospace() << "\t" << "CleanupGeneric" << "\t\t" << instr->cleanup.reg;
+ break;
+ case Instr::ConvertGenericToReal:
+ qWarning().nospace() << "\t" << "ConvertGenericToReal" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src;
+ break;
+ case Instr::ConvertGenericToBool:
+ qWarning().nospace() << "\t" << "ConvertGenericToBool" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src;
+ break;
+ case Instr::ConvertGenericToString:
+ qWarning().nospace() << "\t" << "ConvertGenericToString" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src;
+ break;
+ case Instr::ConvertGenericToUrl:
+ qWarning().nospace() << "\t" << "ConvertGenericToUrl" << "\t" << instr->unaryop.output << "\t" << instr->unaryop.src;
+ break;
+ default:
+ qWarning().nospace() << "\t" << "Unknown";
+ break;
+ }
+}
+
+void QDeclarativeCompiledBindingsPrivate::run(int instrIndex,
+ QDeclarativeContextData *context, QDeclarativeDelayedError *error,
+ QObject *scope, QObject *output, QDeclarativePropertyPrivate::WriteFlags storeFlags)
+{
+ Q_Q(QDeclarativeCompiledBindings);
+
+ error->removeError();
+
+ Register registers[32];
+
+ QDeclarativeEnginePrivate *engine = QDeclarativeEnginePrivate::get(context->engine);
+ Program *program = (Program *)programData;
+ const Instr *instr = program->instructions();
+ instr += instrIndex;
+ const char *data = program->data();
+
+#ifdef QML_THREADED_INTERPRETER
+ static void *decode_instr[] = {
+ FOR_EACH_QML_INSTR(QML_INSTR_ADDR)
+ };
+
+ if (!program->compiled) {
+ program->compiled = true;
+ const Instr *inop = program->instructions();
+ for (int i = 0; i < program->instructionCount; ++i) {
+ Instr *op = (Instr *) inop++;
+ op->common.code = decode_instr[op->common.type];
+ }
+ }
+
+ goto *instr->common.code;
+#else
+ // return;
+
+#ifdef COMPILEDBINDINGS_DEBUG
+ qWarning().nospace() << "Begin binding run";
+#endif
+
+ while (instr) {
+ switch (instr->common.type) {
+
+#ifdef COMPILEDBINDINGS_DEBUG
+ dumpInstruction(instr);
+#endif
+
+#endif
+
+ QML_BEGIN_INSTR(Noop)
+ QML_END_INSTR(Noop)
+
+ QML_BEGIN_INSTR(BindingId)
+ QML_END_INSTR(BindingId)
+
+ QML_BEGIN_INSTR(SubscribeId)
+ subscribeId(context, instr->subscribe.index, instr->subscribe.offset);
+ QML_END_INSTR(SubscribeId)
+
+ QML_BEGIN_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);
+ }
+ QML_END_INSTR(Subscribe)
+
+ QML_BEGIN_INSTR(FetchAndSubscribe)
+ {
+ const Register &input = registers[instr->fetchAndSubscribe.objectReg];
+ Register &output = registers[instr->fetchAndSubscribe.output];
+
+ if (input.isUndefined()) {
+ throwException(instr->fetchAndSubscribe.exceptionId, error, program, context);
+ return;
+ }
+
+ QObject *object = input.getQObject();
+ if (!object) {
+ output.setUndefined();
+ } else {
+ int subIdx = instr->fetchAndSubscribe.subscription;
+ QDeclarativeCompiledBindingsPrivate::Subscription *sub = 0;
+ if (subIdx != -1) {
+ sub = (subscriptions + subIdx);
+ sub->target = q;
+ sub->targetMethod = methodCount + subIdx;
+ }
+ fastProperties()->accessor(instr->fetchAndSubscribe.function)(object, output.typeDataPtr(), sub);
+ }
+ }
+ QML_END_INSTR(FetchAndSubscribe)
+
+ QML_BEGIN_INSTR(LoadId)
+ registers[instr->load.reg].setQObject(context->idValues[instr->load.index].data());
+ QML_END_INSTR(LoadId)
+
+ QML_BEGIN_INSTR(LoadScope)
+ registers[instr->load.reg].setQObject(scope);
+ QML_END_INSTR(LoadScope)
+
+ QML_BEGIN_INSTR(LoadRoot)
+ registers[instr->load.reg].setQObject(context->contextObject);
+ QML_END_INSTR(LoadRoot)
+
+ QML_BEGIN_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.id,
+ registers[instr->attached.reg].getQObject(),
+ true);
+ Q_ASSERT(attached);
+ output.setQObject(attached);
+ }
+ }
+ QML_END_INSTR(LoadAttached)
+
+ QML_BEGIN_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()));
+ }
+ QML_END_INSTR(ConvertIntToReal)
+
+ QML_BEGIN_INSTR(ConvertRealToInt)
+ {
+ const Register &input = registers[instr->unaryop.src];
+ Register &output = registers[instr->unaryop.output];
+ if (input.isUndefined()) output.setUndefined();
+ else output.setint(qRound(input.getqreal()));
+ }
+ QML_END_INSTR(ConvertRealToInt)
+
+ QML_BEGIN_INSTR(Real)
+ registers[instr->real_value.reg].setqreal(instr->real_value.value);
+ QML_END_INSTR(Real)
+
+ QML_BEGIN_INSTR(Int)
+ registers[instr->int_value.reg].setint(instr->int_value.value);
+ QML_END_INSTR(Int)
+
+ QML_BEGIN_INSTR(Bool)
+ registers[instr->bool_value.reg].setbool(instr->bool_value.value);
+ QML_END_INSTR(Bool)
+
+ QML_BEGIN_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);
+ }
+ QML_END_INSTR(String)
+
+ QML_BEGIN_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());
+ }
+ QML_END_INSTR(AddReal)
+
+ QML_BEGIN_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());
+ }
+ QML_END_INSTR(AddInt)
+
+ QML_BEGIN_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);
+ }
+ }
+ QML_END_INSTR(AddString)
+
+ QML_BEGIN_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());
+ }
+ QML_END_INSTR(MinusReal)
+
+ QML_BEGIN_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());
+ }
+ QML_END_INSTR(MinusInt)
+
+ QML_BEGIN_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());
+ }
+ QML_END_INSTR(CompareReal)
+
+ QML_BEGIN_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());
+ }
+ QML_END_INSTR(CompareString)
+
+ QML_BEGIN_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());
+ }
+ QML_END_INSTR(NotCompareReal)
+
+ QML_BEGIN_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());
+ }
+ QML_END_INSTR(NotCompareString)
+
+ QML_BEGIN_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());
+ }
+ QML_END_INSTR(GreaterThanReal)
+
+ QML_BEGIN_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()));
+ }
+ QML_END_INSTR(MaxReal)
+
+ QML_BEGIN_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()));
+ }
+ QML_END_INSTR(MinReal)
+
+ QML_BEGIN_INSTR(NewString)
+ {
+ Register &output = registers[instr->construct.reg];
+ new (output.getstringptr()) QString;
+ output.settype(QMetaType::QString);
+ }
+ QML_END_INSTR(NewString)
+
+ QML_BEGIN_INSTR(NewUrl)
+ {
+ Register &output = registers[instr->construct.reg];
+ new (output.geturlptr()) QUrl;
+ output.settype(QMetaType::QUrl);
+ }
+ QML_END_INSTR(NewUrl)
+
+ QML_BEGIN_INSTR(CleanupString)
+ registers[instr->cleanup.reg].getstringptr()->~QString();
+#ifdef REGISTER_CLEANUP_DEBUG
+ registers[instr->cleanup.reg].setUndefined();
+#endif
+ QML_END_INSTR(CleanupString)
+
+ QML_BEGIN_INSTR(CleanupUrl)
+ registers[instr->cleanup.reg].geturlptr()->~QUrl();
+#ifdef REGISTER_CLEANUP_DEBUG
+ registers[instr->cleanup.reg].setUndefined();
+#endif
+ QML_END_INSTR(CleanupUrl)
+
+ QML_BEGIN_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);
+ }
+ }
+ QML_END_INSTR(Fetch)
+
+ QML_BEGIN_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);
+ }
+ QML_END_INSTR(Store)
+
+ QML_BEGIN_INSTR(Copy)
+ registers[instr->copy.reg] = registers[instr->copy.src];
+ QML_END_INSTR(Copy)
+
+ QML_BEGIN_INSTR(Skip)
+ if (instr->skip.reg == -1 || !registers[instr->skip.reg].getbool())
+ instr += instr->skip.count;
+ QML_END_INSTR(Skip)
+
+ QML_BEGIN_INSTR(Done)
+ return;
+ QML_END_INSTR(Done)
+
+ QML_BEGIN_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);
+ }
+ QML_END_INSTR(InitString)
+
+ QML_BEGIN_INSTR(FindGenericTerminal)
+ // 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,
+ context->parent,
+ identifiers[instr->find.name].identifier,
+ instr->common.type == Instr::FindGenericTerminal);
+ QML_END_INSTR(FindGenericTerminal)
+
+ QML_BEGIN_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,
+ context->parent,
+ identifiers[instr->find.name].identifier,
+ instr->common.type == Instr::FindGenericTerminal);
+ QML_END_INSTR(FindGeneric)
+
+ QML_BEGIN_INSTR(FindPropertyTerminal)
+ {
+ 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,
+ QDeclarativeEnginePrivate::get(context->engine),
+ instr->find.subscribeIndex, identifiers[instr->find.name].identifier,
+ instr->common.type == Instr::FindPropertyTerminal);
+ }
+ QML_END_INSTR(FindPropertyTerminal)
+
+ QML_BEGIN_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,
+ QDeclarativeEnginePrivate::get(context->engine),
+ instr->find.subscribeIndex, identifiers[instr->find.name].identifier,
+ instr->common.type == Instr::FindPropertyTerminal);
+ }
+ QML_END_INSTR(FindProperty)
+
+ QML_BEGIN_INSTR(CleanupGeneric)
+ {
+ int type = registers[instr->cleanup.reg].gettype();
+ if (type == qMetaTypeId<QVariant>()) {
+ registers[instr->cleanup.reg].getvariantptr()->~QVariant();
+#ifdef REGISTER_CLEANUP_DEBUG
+ registers[instr->cleanup.reg].setUndefined();
+#endif
+ } else if (type == QMetaType::QString) {
+ registers[instr->cleanup.reg].getstringptr()->~QString();
+#ifdef REGISTER_CLEANUP_DEBUG
+ registers[instr->cleanup.reg].setUndefined();
+#endif
+ } else if (type == QMetaType::QUrl) {
+ registers[instr->cleanup.reg].geturlptr()->~QUrl();
+#ifdef REGISTER_CLEANUP_DEBUG
+ registers[instr->cleanup.reg].setUndefined();
+#endif
+ }
+ }
+ QML_END_INSTR(CleanupGeneric)
+
+ QML_BEGIN_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();
+ }
+ QML_END_INSTR(ConvertGenericToReal)
+
+ QML_BEGIN_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();
+ }
+ QML_END_INSTR(ConvertGenericToBool)
+
+ QML_BEGIN_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(); }
+ }
+ QML_END_INSTR(ConvertGenericToString)
+
+ QML_BEGIN_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(); }
+ }
+ QML_END_INSTR(ConvertGenericToUrl)
+
+#ifdef QML_THREADED_INTERPRETER
+ // nothing to do
+#else
+ default:
+ qFatal("EEK");
+ break;
+ } // switch
+
+ ++instr;
+ } // while
+#endif
+}
+
+void QDeclarativeBindingCompiler::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--) {
+
+ dumpInstruction(instr);
+ ++instr;
+ }
+}
+
+/*!
+Clear the state associated with attempting to compile a specific binding.
+This does not clear the global "committed binding" states.
+*/
+void QDeclarativeBindingCompilerPrivate::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 QDeclarativeBindingCompilerPrivate::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 QDeclarativeBindingCompilerPrivate::compile(QDeclarativeJS::AST::Node *node)
+{
+ resetInstanceState();
+
+ if (destination->type == -1)
+ return false;
+
+ if (bindingsDump()) {
+ QDeclarativeJS::AST::ExpressionNode *n = node->expressionCast();
+ if (n) {
+ Instr id;
+ id.common.type = Instr::BindingId;
+ id.id.column = n->firstSourceLocation().startColumn;
+ id.id.line = n->firstSourceLocation().startLine;
+ bytecode << id;
+ }
+ }
+
+ Result type;
+
+ if (!parseExpression(node, type))
+ return false;
+
+ if (subscriptionSet.count() > 0xFFFF ||
+ registeredStrings.count() > 0xFFFF)
+ return false;
+
+ if (type.unknownType) {
+ if (!qmlExperimental())
+ return false;
+
+ if (destination->type != QMetaType::QReal &&
+ destination->type != QVariant::String &&
+ destination->type != QMetaType::Bool &&
+ destination->type != QVariant::Url)
+ return false;
+
+ int convertReg = acquireReg();
+ if (convertReg == -1)
+ return false;
+
+ 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;
+
+ } 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 = engine->rawMetaObjectForType(destination->type);
+
+ if (QDeclarativePropertyPrivate::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;
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool QDeclarativeBindingCompilerPrivate::parseExpression(QDeclarativeJS::AST::Node *node, Result &type)
+{
+ while (node->kind == AST::Node::Kind_NestedExpression)
+ node = static_cast<AST::NestedExpression *>(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 QDeclarativeBindingCompilerPrivate::tryName(QDeclarativeJS::AST::Node *node)
+{
+ return node->kind == AST::Node::Kind_IdentifierExpression ||
+ node->kind == AST::Node::Kind_FieldMemberExpression;
+}
+
+bool QDeclarativeBindingCompilerPrivate::parseName(AST::Node *node, Result &type)
+{
+ QStringList nameParts;
+ QList<AST::ExpressionNode *> nameNodes;
+ if (!buildName(nameParts, node, &nameNodes))
+ return false;
+
+ int reg = acquireReg();
+ if (reg == -1)
+ return false;
+ type.reg = reg;
+
+ QDeclarativeParser::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;
+
+ QDeclarativeType *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;
+
+ QDeclarativeImportedNamespace *ns = 0;
+ if (!imports.resolveType(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.id = attachType->attachedPropertiesId();
+ 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)) {
+ QDeclarativeParser::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;
+
+ if (!fetch(type, context->metaObject(), reg, d0Idx, subscribeName, nameNodes.at(ii)))
+ return false;
+ } 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;
+
+ if (!fetch(type, component->metaObject(), reg, d1Idx, subscribeName, nameNodes.at(ii)))
+ return false;
+ } else if (qmlExperimental()) {
+ 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.id = attachType->attachedPropertiesId();
+ 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;
+ if (!fetch(type, mo, reg, idx, subscribeName, nameNodes.at(ii)))
+ 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 QDeclarativeBindingCompilerPrivate::tryArith(QDeclarativeJS::AST::Node *node)
+{
+ if (node->kind != AST::Node::Kind_BinaryExpression)
+ return false;
+
+ AST::BinaryExpression *expression = static_cast<AST::BinaryExpression *>(node);
+ if (expression->op == QSOperator::Add ||
+ expression->op == QSOperator::Sub)
+ return true;
+ else
+ return false;
+}
+
+bool QDeclarativeBindingCompilerPrivate::parseArith(QDeclarativeJS::AST::Node *node, Result &type)
+{
+ AST::BinaryExpression *expression = static_cast<AST::BinaryExpression *>(node);
+
+ type.reg = acquireReg();
+ if (type.reg == -1)
+ return false;
+
+ 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 QDeclarativeBindingCompilerPrivate::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) {
+ if (!qmlExperimental())
+ return false;
+
+ lhsTmp = acquireReg();
+ if (lhsTmp == -1)
+ return false;
+
+ Instr conv;
+ conv.common.type = Instr::ConvertGenericToReal;
+ conv.unaryop.output = lhsTmp;
+ conv.unaryop.src = lhs.reg;
+ bytecode << conv;
+ }
+
+ if (rhs.unknownType) {
+ if (!qmlExperimental())
+ return false;
+
+ rhsTmp = acquireReg();
+ if (rhsTmp == -1)
+ return false;
+
+ 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 QDeclarativeBindingCompilerPrivate::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) {
+ if (!qmlExperimental())
+ return false;
+
+ lhsTmp = acquireReg(Instr::CleanupString);
+ if (lhsTmp == -1)
+ return false;
+
+ Instr convert;
+ convert.common.type = Instr::ConvertGenericToString;
+ convert.unaryop.output = lhsTmp;
+ convert.unaryop.src = lhs.reg;
+ bytecode << convert;
+ }
+
+ if (rhs.unknownType) {
+ if (!qmlExperimental())
+ return false;
+
+ rhsTmp = acquireReg(Instr::CleanupString);
+ if (rhsTmp == -1)
+ return false;
+
+ Instr convert;
+ convert.common.type = Instr::ConvertGenericToString;
+ convert.unaryop.output = rhsTmp;
+ convert.unaryop.src = rhs.reg;
+ bytecode << convert;
+ }
+
+ type.reg = acquireReg(Instr::CleanupString);
+ if (type.reg == -1)
+ return false;
+
+ 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);
+ releaseReg(lhs.reg);
+ releaseReg(rhs.reg);
+
+ return true;
+}
+
+bool QDeclarativeBindingCompilerPrivate::tryLogic(QDeclarativeJS::AST::Node *node)
+{
+ if (node->kind != AST::Node::Kind_BinaryExpression)
+ return false;
+
+ AST::BinaryExpression *expression = static_cast<AST::BinaryExpression *>(node);
+ if (expression->op == QSOperator::Gt ||
+ expression->op == QSOperator::Equal ||
+ expression->op == QSOperator::NotEqual)
+ return true;
+ else
+ return false;
+}
+
+bool QDeclarativeBindingCompilerPrivate::parseLogic(QDeclarativeJS::AST::Node *node, Result &type)
+{
+ AST::BinaryExpression *expression = static_cast<AST::BinaryExpression *>(node);
+
+ Result lhs;
+ Result rhs;
+
+ if (!parseExpression(expression->left, lhs)) return false;
+ if (!parseExpression(expression->right, rhs)) return false;
+
+ type.reg = acquireReg();
+ if (type.reg == -1)
+ return false;
+
+ 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 QDeclarativeBindingCompilerPrivate::tryConditional(QDeclarativeJS::AST::Node *node)
+{
+ return (node->kind == AST::Node::Kind_ConditionalExpression);
+}
+
+bool QDeclarativeBindingCompilerPrivate::parseConditional(QDeclarativeJS::AST::Node *node, Result &type)
+{
+ AST::ConditionalExpression *expression = static_cast<AST::ConditionalExpression *>(node);
+
+ AST::Node *test = expression->expression;
+ if (test->kind == AST::Node::Kind_NestedExpression)
+ test = static_cast<AST::NestedExpression*>(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<QString> 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 QDeclarativeBindingCompilerPrivate::tryConstant(QDeclarativeJS::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 QDeclarativeBindingCompilerPrivate::parseConstant(QDeclarativeJS::AST::Node *node, Result &type)
+{
+ type.metaObject = 0;
+ type.type = -1;
+ type.reg = acquireReg();
+ if (type.reg == -1)
+ return false;
+
+ 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<AST::NumericLiteral *>(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<AST::StringLiteral *>(node)->value->asString();
+ type.type = QMetaType::QString;
+ type.reg = registerLiteralString(str);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool QDeclarativeBindingCompilerPrivate::tryMethod(QDeclarativeJS::AST::Node *node)
+{
+ return node->kind == AST::Node::Kind_CallExpression;
+}
+
+bool QDeclarativeBindingCompilerPrivate::parseMethod(QDeclarativeJS::AST::Node *node, Result &result)
+{
+ AST::CallExpression *expr = static_cast<AST::CallExpression *>(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();
+ if (op.binaryop.output == -1)
+ return false;
+
+ op.binaryop.src1 = r0.reg;
+ op.binaryop.src2 = r1.reg;
+ bytecode << op;
+
+ result.type = QMetaType::QReal;
+ result.reg = op.binaryop.output;
+
+ return true;
+}
+
+bool QDeclarativeBindingCompilerPrivate::buildName(QStringList &name,
+ QDeclarativeJS::AST::Node *node,
+ QList<QDeclarativeJS::AST::ExpressionNode *> *nodes)
+{
+ if (node->kind == AST::Node::Kind_IdentifierExpression) {
+ name << static_cast<AST::IdentifierExpression*>(node)->name->asString();
+ if (nodes) *nodes << static_cast<AST::IdentifierExpression*>(node);
+ } else if (node->kind == AST::Node::Kind_FieldMemberExpression) {
+ AST::FieldMemberExpression *expr =
+ static_cast<AST::FieldMemberExpression *>(node);
+
+ if (!buildName(name, expr->base, nodes))
+ return false;
+
+ name << expr->name->asString();
+ if (nodes) *nodes << expr;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool QDeclarativeBindingCompilerPrivate::fetch(Result &rv, const QMetaObject *mo, int reg,
+ int idx, const QStringList &subName,
+ QDeclarativeJS::AST::ExpressionNode *node)
+{
+ QMetaProperty prop = mo->property(idx);
+ rv.metaObject = 0;
+ rv.type = 0;
+
+ //XXX binding optimizer doesn't handle properties with a revision
+ if (prop.revision() > 0)
+ return false;
+
+ int fastFetchIndex = fastProperties()->accessorIndexForProperty(mo, idx);
+
+ Instr fetch;
+
+ if (!qmlDisableFastProperties() && fastFetchIndex != -1) {
+ fetch.common.type = Instr::FetchAndSubscribe;
+ fetch.fetchAndSubscribe.objectReg = reg;
+ fetch.fetchAndSubscribe.output = reg;
+ fetch.fetchAndSubscribe.function = fastFetchIndex;
+ fetch.fetchAndSubscribe.subscription = subscriptionIndex(subName);
+ fetch.fetchAndSubscribe.exceptionId = exceptionId(node);
+ } else {
+ 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;
+ }
+
+ 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 = engine->metaObjectForType(rv.type);
+ rv.reg = reg;
+
+ if (rv.type == QMetaType::QString) {
+ int tmp = acquireReg();
+ if (tmp == -1)
+ return false;
+ 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<QDeclarativeAnchorLine>() &&
+ rv.type != QMetaType::QString) {
+ rv.metaObject = 0;
+ rv.type = 0;
+ return false; // Unsupported type (string not supported yet);
+ }
+
+ return true;
+}
+
+void QDeclarativeBindingCompilerPrivate::registerCleanup(int reg, int cleanup, int cleanupType)
+{
+ registerCleanups.insert(reg, qMakePair(cleanup, cleanupType));
+}
+
+int QDeclarativeBindingCompilerPrivate::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 QDeclarativeBindingCompilerPrivate::releaseReg(int reg)
+{
+ Q_ASSERT(reg >= 0 && reg <= 31);
+
+ if (registerCleanups.contains(reg)) {
+ QPair<int, int> 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 QDeclarativeBindingCompilerPrivate::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);
+ if (reg == -1)
+ return false;
+
+ 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 QDeclarativeBindingCompilerPrivate::registerString(const QString &string)
+{
+ Q_ASSERT(!string.isEmpty());
+
+ QHash<QString, QPair<int, int> >::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 QDeclarativeBindingCompilerPrivate::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 QDeclarativeBindingCompilerPrivate::subscriptionIndex(const QStringList &sub)
+{
+ QString str = sub.join(QLatin1String("."));
+ QHash<QString, int>::ConstIterator iter = subscriptionIds.find(str);
+ if (iter == subscriptionIds.end())
+ iter = subscriptionIds.insert(str, subscriptionIds.count());
+ usedSubscriptionIds.insert(*iter);
+ return *iter;
+}
+
+/*
+ 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 QDeclarativeBindingCompilerPrivate::subscriptionNeutral(const QSet<QString> &base,
+ const QSet<QString> &lhs,
+ const QSet<QString> &rhs)
+{
+ QSet<QString> difflhs = lhs;
+ difflhs.subtract(rhs);
+ QSet<QString> diffrhs = rhs;
+ diffrhs.subtract(lhs);
+
+ difflhs.unite(diffrhs);
+ difflhs.subtract(base);
+
+ return difflhs.isEmpty();
+}
+
+quint8 QDeclarativeBindingCompilerPrivate::exceptionId(QDeclarativeJS::AST::ExpressionNode *n)
+{
+ quint8 rv = 0xFF;
+ if (n && exceptions.count() < 0xFF) {
+ rv = (quint8)exceptions.count();
+ QDeclarativeJS::AST::SourceLocation l = n->firstSourceLocation();
+ quint64 e = l.startLine;
+ e <<= 32;
+ e |= l.startColumn;
+ exceptions.append(e);
+ }
+ return rv;
+}
+
+QDeclarativeBindingCompiler::QDeclarativeBindingCompiler()
+: d(new QDeclarativeBindingCompilerPrivate)
+{
+}
+
+QDeclarativeBindingCompiler::~QDeclarativeBindingCompiler()
+{
+ delete d; d = 0;
+}
+
+/*
+Returns true if any bindings were compiled.
+*/
+bool QDeclarativeBindingCompiler::isValid() const
+{
+ return !d->committed.bytecode.isEmpty();
+}
+
+/*
+-1 on failure, otherwise the binding index to use.
+*/
+int QDeclarativeBindingCompiler::compile(const Expression &expression, QDeclarativeEnginePrivate *engine)
+{
+ if (!expression.expression.asAST()) return false;
+
+ if (!qmlExperimental() && expression.property->isValueTypeSubProperty)
+ return -1;
+
+ if (qmlDisableOptimizer())
+ return -1;
+
+ 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 QDeclarativeBindingCompilerPrivate::buildSignalTable() const
+{
+ QHash<int, QList<int> > table;
+
+ for (int ii = 0; ii < committed.count(); ++ii) {
+ const QSet<int> &deps = committed.dependencies.at(ii);
+ for (QSet<int>::ConstIterator iter = deps.begin(); iter != deps.end(); ++iter)
+ table[*iter].append(ii);
+ }
+
+ QVector<quint32> header;
+ QVector<quint32> data;
+ for (int ii = 0; ii < committed.subscriptionIds.count(); ++ii) {
+ header.append(committed.subscriptionIds.count() + data.count());
+ const QList<int> &bindings = table[ii];
+ data.append(bindings.count());
+ for (int jj = 0; jj < bindings.count(); ++jj)
+ data.append(bindings.at(jj));
+ }
+ header << data;
+
+ return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32));
+}
+
+QByteArray QDeclarativeBindingCompilerPrivate::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 QDeclarativeBindingCompiler::program() const
+{
+ QByteArray programData;
+
+ if (isValid()) {
+ Program prog;
+ prog.bindings = d->committed.count();
+
+ QVector<Instr> 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();
+ prog.compiled = false;
+ 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