diff options
Diffstat (limited to 'src/declarative/qml/qmlbindingvme.cpp')
-rw-r--r-- | src/declarative/qml/qmlbindingvme.cpp | 2239 |
1 files changed, 2239 insertions, 0 deletions
diff --git a/src/declarative/qml/qmlbindingvme.cpp b/src/declarative/qml/qmlbindingvme.cpp new file mode 100644 index 0000000..16b0a21 --- /dev/null +++ b/src/declarative/qml/qmlbindingvme.cpp @@ -0,0 +1,2239 @@ +/**************************************************************************** +** +** 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 <private/qmlcontext_p.h> +#include <private/qmljsast_p.h> +#include <private/qmljsengine_p.h> +#include <QtCore/qdebug.h> +#include <private/qmlgraphicsanchors_p_p.h> + +QT_BEGIN_NAMESPACE + +using namespace QmlJS; + +namespace { +// Supported types: int, qreal, QString (needs constr/destr), QObject*, bool +struct Register { + void setQObject(QObject *o) { *((QObject **)data) = o; } + QObject *getQObject() { return *((QObject **)data); } + + void setqreal(qreal v) { *((qreal *)data) = v; } + qreal getqreal() { return *((qreal *)data); } + + void setint(int v) { *((int *)data) = v; } + int getint() { return *((int *)data); } + + void setbool(bool v) { *((bool *)data) = v; } + bool getbool() { return *((bool *)data); } + + QVariant *getvariantptr() { return (QVariant *)typeDataPtr(); } + QString *getstringptr() { return (QString *)typeDataPtr(); } + QUrl *geturlptr() { return (QUrl *)typeDataPtr(); } + + void *typeDataPtr() { return (void *)&data; } + void *typeMemory() { return (void *)data; } + + int gettype() { 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 packing[1]; + quint32 index; + } attached; + struct { + quint8 type; + qint8 output; + qint8 reg; + quint8 packing[1]; + quint32 index; + } store; + struct { + quint8 type; + qint8 output; + qint8 objectReg; + quint8 packing[1]; + 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 packing[1]; + 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; + 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<QString> subscriptionSet; + }; + + QmlBindingCompilerPrivate() : registers(0) {} + + void resetInstanceState(); + int commitCompile(); + + QmlParser::Object *context; + QmlParser::Object *component; + QmlParser::Property *destination; + QHash<QString, QmlParser::Object *> 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 *); + bool fetch(Result &type, const QMetaObject *, int reg, int idx, const QStringList &); + + 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); + + 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; + + QHash<QString, QPair<int, int> > registeredStrings; + + int count() const { return offsets.count(); } + } committed; + + QByteArray buildSignalTable() const; +}; + +inline void subscribe(QObject *o, int notifyIndex, + int subIndex, QmlBindingVME::Config *config) +{ + QmlBindingVME::Config::Subscription *s = config->subscriptions + subIndex; + if (o != s->source || notifyIndex != s->notifyIndex) { + if (s->source) + QMetaObject::disconnectOne(s->source, s->notifyIndex, + config->target, config->targetSlot + subIndex); + 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); + } +} + +static QObject *variantToQObject(const QVariant &value, bool *ok) +{ + *ok = false; + Q_UNUSED(value); + 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) + 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>()) { + 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) return false; + output->settype(QMetaType::QObjectStar); + } + + } else { + if (!isTerminal) + return false; + + 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 { + return false; + } +} + +static bool findproperty(Register *input, + Register *output, + QmlEnginePrivate *enginePriv, + QmlBindingVME::Config *config, int subIdx, + const QScriptDeclarativeClass::Identifier &name, + bool isTerminal) +{ + return findproperty(input->getQObject(), output, enginePriv, + config, subIdx, name, isTerminal); +} + +static bool 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<QVariant>()); + } else { + bool ok; + output->setQObject(variantToQObject(value, &ok)); + if (!ok) return false; + output->settype(QMetaType::QObjectStar); + } + } + + return true; + } + + 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"); + return false; + } + } + + if (QObject *root = context->defaultObjects.isEmpty()?0:context->defaultObjects.first()) { + + if (findproperty(root, output, enginePriv, config, subIdx, name, isTerminal)) + return true; + + } + + if (context->parent) { + context = QmlContextPrivate::get(context->parent); + } else { + context = 0; + } + } + + return false; +} + + +// 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 == 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, QmlContextPrivate *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; +} + +/*! +Returns the signal/binding table. +*/ +void QmlBindingVME::init(const char *programData, Config *config, + quint32 **sigTable, quint32 *bindingCount) +{ + Program *program = (Program *)programData; + if (program->subscriptions) + config->subscriptions = new Config::Subscription[program->subscriptions]; + if (program->identifiers) + config->identifiers = new QScriptDeclarativeClass::PersistentIdentifier[program->identifiers]; + + *sigTable = (quint32 *)(program->data() + program->signalTableOffset); + *bindingCount = program->bindings; +} + +void QmlBindingVME::run(const char *programData, int instrIndex, + Config *config, QmlContextPrivate *context, + QObject **scopes, QObject **outputs) +{ + Register registers[32]; + 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: + case Instr::Subscribe: + { + QObject *o = registers[instr->subscribe.reg].getQObject(); + int notifyIndex = instr->subscribe.index; + + if (instr->common.type == Instr::SubscribeId) { + o = QmlContextPrivate::get(context); + notifyIndex += context->notifyIndex; + } + + 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(scopes[instr->load.index]); + break; + + case Instr::LoadRoot: + registers[instr->load.reg].setQObject(context->defaultObjects.at(0)); + break; + + case Instr::LoadAttached: + { + QObject *o = qmlAttachedPropertiesObjectById(instr->attached.index, + registers[instr->attached.reg].getQObject(), + true); + registers[instr->attached.output].setQObject(o); + } + break; + + case Instr::ConvertIntToReal: + registers[instr->unaryop.output].setqreal(qreal(registers[instr->unaryop.src].getint())); + break; + + case Instr::ConvertRealToInt: + registers[instr->unaryop.output].setint(int(registers[instr->unaryop.src].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: + new (registers[instr->bool_value.reg].getstringptr()) + QString((QChar *)(data + instr->string_value.offset), instr->string_value.length); + break; + + case Instr::AddReal: + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.src1].getqreal() + + registers[instr->binaryop.src2].getqreal()); + break; + + case Instr::AddInt: + registers[instr->binaryop.output].setint(registers[instr->binaryop.src1].getint() + + registers[instr->binaryop.src2].getint()); + break; + + case Instr::AddString: + new (registers[instr->binaryop.output].getstringptr()) + QString(*registers[instr->binaryop.src1].getstringptr() + + *registers[instr->binaryop.src2].getstringptr()); + break; + + case Instr::MinusReal: + registers[instr->binaryop.output].setqreal(registers[instr->binaryop.src1].getqreal() - + registers[instr->binaryop.src2].getqreal()); + break; + + case Instr::MinusInt: + registers[instr->binaryop.output].setint(registers[instr->binaryop.src1].getint() - + registers[instr->binaryop.src2].getint()); + break; + + case Instr::CompareReal: + registers[instr->binaryop.output].setbool(registers[instr->binaryop.src1].getqreal() == + registers[instr->binaryop.src2].getqreal()); + break; + + case Instr::CompareString: + registers[instr->binaryop.output].setbool(*registers[instr->binaryop.src1].getstringptr() == + *registers[instr->binaryop.src2].getstringptr()); + break; + + case Instr::NotCompareReal: + registers[instr->binaryop.output].setbool(registers[instr->binaryop.src1].getqreal() != + registers[instr->binaryop.src2].getqreal()); + break; + + case Instr::NotCompareString: + registers[instr->binaryop.output].setbool(*registers[instr->binaryop.src1].getstringptr() != + *registers[instr->binaryop.src2].getstringptr()); + break; + + case Instr::GreaterThanReal: + registers[instr->binaryop.output].setbool(registers[instr->binaryop.src1].getqreal() > + registers[instr->binaryop.src2].getqreal()); + break; + case Instr::MaxReal: + registers[instr->binaryop.output].setqreal(qMax(registers[instr->binaryop.src1].getqreal(), + registers[instr->binaryop.src2].getqreal())); + break; + case Instr::MinReal: + registers[instr->binaryop.output].setqreal(qMin(registers[instr->binaryop.src1].getqreal(), + registers[instr->binaryop.src2].getqreal())); + break; + case Instr::NewString: + new (registers[instr->construct.reg].typeMemory()) QString; + break; + + case Instr::NewUrl: + new (registers[instr->construct.reg].typeMemory()) 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: + { + QObject *object = registers[instr->fetch.objectReg].getQObject(); + if (!object) { + qWarning() << "ERROR - Fetch"; + return; + } + void *argv[] = { registers[instr->fetch.output].typeDataPtr(), 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, instr->fetch.index, argv); + } + break; + + case Instr::Store: + { + int status = -1; + void *argv[] = { registers[instr->store.reg].typeDataPtr(), + 0, &status, &storeFlags }; + QMetaObject::metacall(outputs[instr->store.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 + if (!findgeneric(registers + instr->find.reg, config, instr->find.subscribeIndex, + QmlContextPrivate::get(context->parent), + config->identifiers[instr->find.name].identifier, + instr->common.type == Instr::FindGenericTerminal)) { + qWarning() << "ERROR - FindGeneric*"; + return; + } + break; + + case Instr::FindPropertyTerminal: + case Instr::FindProperty: + if (!findproperty(registers + instr->find.src, registers + instr->find.reg, + QmlEnginePrivate::get(context->engine), + config, instr->find.subscribeIndex, + config->identifiers[instr->find.name].identifier, + instr->common.type == Instr::FindPropertyTerminal)) { + qWarning() << "ERROR - FindProperty*"; + return; + } + break; + + case Instr::CleanupGeneric: + { + int type = registers[instr->cleanup.reg].gettype(); + if (type == qMetaTypeId<QVariant>()) { + 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: + { + int type = registers[instr->unaryop.src].gettype(); + registers[instr->unaryop.output].setqreal(toReal(registers + instr->unaryop.src, type)); + } + break; + + case Instr::ConvertGenericToBool: + { + int type = registers[instr->unaryop.src].gettype(); + registers[instr->unaryop.output].setbool(toBool(registers + instr->unaryop.src, type)); + } + break; + + case Instr::ConvertGenericToString: + { + int type = registers[instr->unaryop.src].gettype(); + void *regPtr = registers[instr->unaryop.output].typeDataPtr(); + new (regPtr) QString(toString(registers + instr->unaryop.src, type)); + } + break; + + case Instr::ConvertGenericToUrl: + { + int type = registers[instr->unaryop.src].gettype(); + void *regPtr = registers[instr->unaryop.output].typeDataPtr(); + new (regPtr) QUrl(toUrl(registers + instr->unaryop.src, type, context)); + } + 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; + 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.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; + 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; + 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<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 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; + if (!buildName(nameParts, node)) + 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(); + 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); + } 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); + } 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); + + 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); + 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); + 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<AST::BinaryExpression *>(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<AST::BinaryExpression *>(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<AST::BinaryExpression *>(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<AST::BinaryExpression *>(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<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 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<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 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<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(); + 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) +{ + if (node->kind == AST::Node::Kind_IdentifierExpression) { + name << static_cast<AST::IdentifierExpression*>(node)->name->asString(); + } else if (node->kind == AST::Node::Kind_FieldMemberExpression) { + AST::FieldMemberExpression *expr = + static_cast<AST::FieldMemberExpression *>(node); + + if (!buildName(name, expr->base)) + return false; + + name << expr->name->asString(); + } else { + return false; + } + + return true; +} + + +bool QmlBindingCompilerPrivate::fetch(Result &rv, const QMetaObject *mo, int reg, int idx, const QStringList &subName) +{ + 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; + + 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<QmlGraphicsAnchorLine>() && + 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<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 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<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 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<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 QmlBindingCompilerPrivate::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(); +} + + +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 QmlBasicScript::Expression &expression, + QmlEnginePrivate *engine) +{ + if (!expression.expression.asAST()) return false; + + d->context = expression.context; + d->component = expression.component; + d->destination = expression.property; + d->ids = expression.ids; + d->imports = expression.imports; + d->engine = engine; + + if (d->compile(expression.expression.asAST())) { + return d->commitCompile(); + } else { + return -1; + } +} + + +QByteArray QmlBindingCompilerPrivate::buildSignalTable() const +{ + QHash<int, QList<int> > table; + + for (int ii = 0; ii < committed.count(); ++ii) { + const QSet<int> &deps = committed.dependencies.at(ii); + for (QSet<int>::ConstIterator iter = deps.begin(); iter != deps.end(); ++iter) + table[*iter].append(ii); + } + + QVector<quint32> header; + QVector<quint32> data; + for (int ii = 0; ii < committed.subscriptionIds.count(); ++ii) { + header.append(committed.subscriptionIds.count() + data.count()); + const QList<int> &bindings = table[ii]; + data.append(bindings.count()); + for (int jj = 0; jj < bindings.count(); ++jj) + data.append(bindings.at(jj)); + } + header << data; + + return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32)); +} + +/* +Returns the compiled program. +*/ +QByteArray QmlBindingCompiler::program() const +{ + QByteArray programData; + + if (isValid()) { + Program prog; + prog.bindings = d->committed.count(); + + QVector<Instr> bytecode; + Instr skip; + skip.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(); + + 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 + |