diff options
author | Aaron Kennedy <aaron.kennedy@nokia.com> | 2010-01-22 05:37:39 (GMT) |
---|---|---|
committer | Aaron Kennedy <aaron.kennedy@nokia.com> | 2010-01-22 08:01:48 (GMT) |
commit | 3a75eeb5da34fbb50173c3f610c08d160c21f4e3 (patch) | |
tree | ab854f509d3a3720b74d2ef9d5e7803881078673 | |
parent | 0fe084df052b8ac66918d0b2732b65f5569881c0 (diff) | |
download | Qt-3a75eeb5da34fbb50173c3f610c08d160c21f4e3.zip Qt-3a75eeb5da34fbb50173c3f610c08d160c21f4e3.tar.gz Qt-3a75eeb5da34fbb50173c3f610c08d160c21f4e3.tar.bz2 |
Harden QML binding optimizer
-rw-r--r-- | src/declarative/qml/qmlbinding.cpp | 37 | ||||
-rw-r--r-- | src/declarative/qml/qmlbinding_p.h | 4 | ||||
-rw-r--r-- | src/declarative/qml/qmlbindingoptimizations.cpp | 8 | ||||
-rw-r--r-- | src/declarative/qml/qmlbindingoptimizations_p.h | 2 | ||||
-rw-r--r-- | src/declarative/qml/qmlbindingvme.cpp | 668 | ||||
-rw-r--r-- | src/declarative/qml/qmlbindingvme_p.h | 4 | ||||
-rw-r--r-- | src/declarative/qml/qmlengine_p.h | 4 | ||||
-rw-r--r-- | src/declarative/qml/qmlexpression.cpp | 14 | ||||
-rw-r--r-- | src/declarative/qml/qmlexpression_p.h | 27 |
9 files changed, 489 insertions, 279 deletions
diff --git a/src/declarative/qml/qmlbinding.cpp b/src/declarative/qml/qmlbinding.cpp index 4ec7191..c0389a8 100644 --- a/src/declarative/qml/qmlbinding.cpp +++ b/src/declarative/qml/qmlbinding.cpp @@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE QML_DEFINE_NOCREATE_TYPE(QmlBinding); QmlBindingData::QmlBindingData() -: updating(false), enabled(false), nextError(0), prevError(0) +: updating(false), enabled(false) { } @@ -78,37 +78,6 @@ void QmlBindingData::refresh() } } -void QmlBindingData::removeError() -{ - if (!prevError) return; - - if (nextError) nextError->prevError = prevError; - *prevError = nextError; - nextError = 0; - prevError = 0; -} - -bool QmlBindingData::addError() -{ - if (prevError) return false; - - QmlContext *c = context(); - if (!c) return false; - QmlEngine *e = c->engine(); - if (!e) return false; - - QmlEnginePrivate *p = QmlEnginePrivate::get(e); - - if (p->inProgressCreations == 0) return false; // Not in construction - - prevError = &p->erroredBindings; - nextError = p->erroredBindings; - p->erroredBindings = this; - if (nextError) nextError->prevError = &nextError; - - return true; -} - QmlBindingPrivate::QmlBindingPrivate() : QmlExpressionPrivate(new QmlBindingData) { @@ -210,7 +179,9 @@ void QmlBinding::update(QmlMetaProperty::WriteFlags flags) } if (data->error.isValid()) { - if (!data->addError()) + QmlEnginePrivate *p = (data->context() && data->context()->engine())? + QmlEnginePrivate::get(data->context()->engine()):0; + if (!data->addError(p)) qWarning().nospace() << qPrintable(this->error().toString()); } else { data->removeError(); diff --git a/src/declarative/qml/qmlbinding_p.h b/src/declarative/qml/qmlbinding_p.h index 945b659..c6c1935 100644 --- a/src/declarative/qml/qmlbinding_p.h +++ b/src/declarative/qml/qmlbinding_p.h @@ -72,10 +72,6 @@ public: QmlMetaProperty property; virtual void refresh(); - void removeError(); - bool addError(); - QmlBindingData *nextError; - QmlBindingData **prevError; }; class QmlBindingPrivate : public QmlExpressionPrivate diff --git a/src/declarative/qml/qmlbindingoptimizations.cpp b/src/declarative/qml/qmlbindingoptimizations.cpp index 868440e..beee3c9 100644 --- a/src/declarative/qml/qmlbindingoptimizations.cpp +++ b/src/declarative/qml/qmlbindingoptimizations.cpp @@ -159,14 +159,14 @@ void QmlOptimizedBindings::run(Binding *binding) vt->read(binding->target, binding->property & 0xFFFF); QObject *target = vt; - QmlBindingVME::run(m_program, binding->index, &m_config, cp, - &binding->scope, &target); + QmlBindingVME::run(m_program, binding->index, &m_config, cp, binding, + binding->scope, target); vt->write(binding->target, binding->property & 0xFFFF, QmlMetaProperty::DontRemoveBinding); } else { - QmlBindingVME::run(m_program, binding->index, &m_config, cp, - &binding->scope, &binding->target); + QmlBindingVME::run(m_program, binding->index, &m_config, cp, binding, + binding->scope, binding->target); } } diff --git a/src/declarative/qml/qmlbindingoptimizations_p.h b/src/declarative/qml/qmlbindingoptimizations_p.h index feb753e..59a0cc6 100644 --- a/src/declarative/qml/qmlbindingoptimizations_p.h +++ b/src/declarative/qml/qmlbindingoptimizations_p.h @@ -73,7 +73,7 @@ protected: int qt_metacall(QMetaObject::Call, int, void **); private: - struct Binding : public QmlAbstractBinding { + struct Binding : public QmlAbstractBinding, public QmlDelayedError { Binding() : enabled(false), updating(0), property(0), scope(0), target(0), parent(0) {} diff --git a/src/declarative/qml/qmlbindingvme.cpp b/src/declarative/qml/qmlbindingvme.cpp index da57452..5efaaca 100644 --- a/src/declarative/qml/qmlbindingvme.cpp +++ b/src/declarative/qml/qmlbindingvme.cpp @@ -43,7 +43,9 @@ #include <private/qmlcontext_p.h> #include <private/qmljsast_p.h> #include <private/qmljsengine_p.h> +#include <private/qmlexpression_p.h> #include <QtCore/qdebug.h> +#include <QtCore/qnumeric.h> #include <private/qmlgraphicsanchors_p_p.h> QT_BEGIN_NAMESPACE @@ -53,26 +55,36 @@ 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 setUndefined() { type = 0; } + void setUnknownButDefined() { type = -1; } + void setNaN() { setqreal(qSNaN()); } + bool isUndefined() const { return type == 0; } - void setqreal(qreal v) { *((qreal *)data) = v; } - qreal getqreal() { return *((qreal *)data); } + void setQObject(QObject *o) { *((QObject **)data) = o; type = QMetaType::QObjectStar; } + QObject *getQObject() const { return *((QObject **)data); } - void setint(int v) { *((int *)data) = v; } - int getint() { return *((int *)data); } + void setqreal(qreal v) { *((qreal *)data) = v; type = QMetaType::QReal; } + qreal getqreal() const { return *((qreal *)data); } - void setbool(bool v) { *((bool *)data) = v; } - bool getbool() { return *((bool *)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() { return type; } + int gettype() const { return type; } void settype(int t) { type = t; } int type; // Optional type @@ -171,21 +183,21 @@ struct Instr { quint8 type; qint8 output; qint8 reg; - quint8 packing[1]; + quint8 exceptionId; quint32 index; } attached; struct { quint8 type; qint8 output; qint8 reg; - quint8 packing[1]; + quint8 exceptionId; quint32 index; } store; struct { quint8 type; qint8 output; qint8 objectReg; - quint8 packing[1]; + quint8 exceptionId; quint32 index; } fetch; struct { @@ -246,7 +258,7 @@ struct Instr { quint8 type; qint8 reg; qint8 src; - quint8 packing[1]; + quint8 exceptionId; quint16 name; quint16 subscribeIndex; } find; @@ -268,6 +280,7 @@ struct Program { quint32 bindings; quint32 dataLength; quint32 signalTableOffset; + quint32 exceptionDataOffset; quint16 subscriptions; quint16 identifiers; quint16 instructionCount; @@ -337,8 +350,8 @@ struct QmlBindingCompilerPrivate 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 &); + bool buildName(QStringList &, QmlJS::AST::Node *, QList<QmlJS::AST::ExpressionNode *> *nodes = 0); + bool fetch(Result &type, const QMetaObject *, int reg, int idx, const QStringList &, QmlJS::AST::ExpressionNode *); quint32 registers; QHash<int, QPair<int, int> > registerCleanups; @@ -355,6 +368,9 @@ struct QmlBindingCompilerPrivate int subscriptionIndex(const QStringList &); bool subscriptionNeutral(const QSet<QString> &base, const QSet<QString> &lhs, const QSet<QString> &rhs); + quint8 exceptionId(QmlJS::AST::ExpressionNode *); + QVector<quint64> exceptions; + QSet<int> usedSubscriptionIds; QSet<QString> subscriptionSet; QHash<QString, int> subscriptionIds; @@ -368,6 +384,7 @@ struct QmlBindingCompilerPrivate QVector<Instr> bytecode; QByteArray data; QHash<QString, int> subscriptionIds; + QVector<quint64> exceptions; QHash<QString, QPair<int, int> > registeredStrings; @@ -375,6 +392,7 @@ struct QmlBindingCompilerPrivate } committed; QByteArray buildSignalTable() const; + QByteArray buildExceptionData() const; }; inline void subscribe(QObject *o, int notifyIndex, @@ -399,11 +417,94 @@ inline void subscribe(QObject *o, int notifyIndex, } } +// 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, 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; +} + static QObject *variantToQObject(const QVariant &value, bool *ok) { - *ok = false; - Q_UNUSED(value); - return 0; + if (ok) *ok = true; + + if (value.userType() == QMetaType::QObjectStar) { + return qvariant_cast<QObject*>(value); + } else { + if (ok) *ok = false; + return 0; + } } static QmlPropertyCache::Data *findproperty(QObject *obj, @@ -441,8 +542,10 @@ static bool findproperty(QObject *obj, Register *output, const QScriptDeclarativeClass::Identifier &name, bool isTerminal) { - if (!obj) + if (!obj) { + output->setUndefined(); return false; + } QmlPropertyCache::Data local; QmlPropertyCache::Data *property = findproperty(obj, name, enginePriv, local); @@ -466,15 +569,16 @@ static bool findproperty(QObject *obj, Register *output, } else { bool ok; output->setQObject(variantToQObject(v, &ok)); - if (!ok) return false; - output->settype(QMetaType::QObjectStar); + if (!ok) + output->setUndefined(); + else + output->settype(QMetaType::QObjectStar); } } else { - if (!isTerminal) - return false; - - if (property->propType == QMetaType::QReal) { + 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); @@ -496,27 +600,16 @@ static bool findproperty(QObject *obj, Register *output, QVariant(obj->metaObject()->property(property->coreIndex).read(obj)); output->settype(qMetaTypeId<QVariant>()); } - } return true; } else { + output->setUndefined(); 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 +static void findgeneric(Register *output, // value output QmlBindingVME::Config *config, int subIdx, // Subscription index in config QmlContextPrivate *context, // Context to search in @@ -547,26 +640,26 @@ static bool findgeneric(Register *output, // val } else { bool ok; output->setQObject(variantToQObject(value, &ok)); - if (!ok) return false; - output->settype(QMetaType::QObjectStar); + if (!ok) { output->setUndefined(); } + else { output->settype(QMetaType::QObjectStar); } + return; } } - return true; + return; } for (int ii = 0; ii < context->scripts.count(); ++ii) { QScriptValue function = QScriptDeclarativeClass::function(context->scripts.at(ii), name); if (function.isValid()) { qFatal("Binding optimizer resolved name to QScript method"); - return false; } } if (QObject *root = context->defaultObjects.isEmpty()?0:context->defaultObjects.first()) { if (findproperty(root, output, enginePriv, config, subIdx, name, isTerminal)) - return true; + return; } @@ -577,86 +670,7 @@ static bool findgeneric(Register *output, // val } } - 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 == 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, 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; + output->setUndefined(); } /*! @@ -675,10 +689,29 @@ void QmlBindingVME::init(const char *programData, Config *config, *bindingCount = program->bindings; } +static void throwException(int id, QmlDelayedError *error, + Program *program, QmlContextPrivate *context) +{ + error->error.setUrl(context->url); + error->error.setDescription("TypeError: Result of expression is not an object"); + if (id != 0xFF) { + quint64 e = *((quint64 *)(program->data() + program->exceptionDataOffset) + id); + error->error.setLine((e >> 32) & 0xFFFFFFFF); + error->error.setColumn(e & 0xFFFFFFFF); + } else { + error->error.setLine(-1); + error->error.setColumn(-1); + } + if (!context->engine || !error->addError(QmlEnginePrivate::get(context->engine))) + qWarning() << error->error; +} + void QmlBindingVME::run(const char *programData, int instrIndex, - Config *config, QmlContextPrivate *context, - QObject **scopes, QObject **outputs) + Config *config, QmlContextPrivate *context, QmlDelayedError *error, + QObject *scope, QObject *output) { + error->removeError(); + Register registers[32]; int storeFlags = 0; @@ -696,17 +729,19 @@ void QmlBindingVME::run(const char *programData, int instrIndex, case Instr::SubscribeId: case Instr::Subscribe: - { - QObject *o = registers[instr->subscribe.reg].getQObject(); - int notifyIndex = instr->subscribe.index; + { + QObject *o = 0; + 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); + if (instr->common.type == Instr::SubscribeId) { + o = QmlContextPrivate::get(context); + notifyIndex += context->notifyIndex; + } else { + const Register &object = registers[instr->subscribe.reg]; + if (!object.isUndefined()) o = object.getQObject(); } + subscribe(o, instr->subscribe.index, instr->subscribe.offset, config); + } break; case Instr::LoadId: @@ -714,7 +749,7 @@ void QmlBindingVME::run(const char *programData, int instrIndex, break; case Instr::LoadScope: - registers[instr->load.reg].setQObject(scopes[instr->load.index]); + registers[instr->load.reg].setQObject(scope); break; case Instr::LoadRoot: @@ -722,20 +757,44 @@ void QmlBindingVME::run(const char *programData, int instrIndex, break; case Instr::LoadAttached: - { - QObject *o = qmlAttachedPropertiesObjectById(instr->attached.index, - registers[instr->attached.reg].getQObject(), - true); - registers[instr->attached.output].setQObject(o); + { + const Register &input = registers[instr->attached.reg]; + Register &output = registers[instr->attached.output]; + if (input.isUndefined()) { + throwException(instr->attached.exceptionId, error, program, context); + return; + } + + QObject *object = registers[instr->attached.reg].getQObject(); + if (!object) { + output.setUndefined(); + } else { + QObject *attached = + qmlAttachedPropertiesObjectById(instr->attached.index, + registers[instr->attached.reg].getQObject(), + true); + Q_ASSERT(attached); + output.setQObject(attached); } + } break; case Instr::ConvertIntToReal: - registers[instr->unaryop.output].setqreal(qreal(registers[instr->unaryop.src].getint())); + { + const Register &input = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (input.isUndefined()) output.setUndefined(); + else output.setqreal(qreal(input.getint())); + } break; case Instr::ConvertRealToInt: - registers[instr->unaryop.output].setint(int(registers[instr->unaryop.src].getqreal())); + { + const Register &input = registers[instr->unaryop.src]; + Register &output = registers[instr->unaryop.output]; + if (input.isUndefined()) output.setUndefined(); + else output.setint(int(input.getqreal())); + } break; case Instr::Real: @@ -751,74 +810,160 @@ void QmlBindingVME::run(const char *programData, int instrIndex, break; case Instr::String: - new (registers[instr->bool_value.reg].getstringptr()) + { + Register &output = registers[instr->string_value.reg]; + new (output.getstringptr()) QString((QChar *)(data + instr->string_value.offset), instr->string_value.length); + output.settype(QMetaType::QString); + } break; case Instr::AddReal: - registers[instr->binaryop.output].setqreal(registers[instr->binaryop.src1].getqreal() + - registers[instr->binaryop.src2].getqreal()); + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setqreal(lhs.getqreal() + rhs.getqreal()); + } break; case Instr::AddInt: - registers[instr->binaryop.output].setint(registers[instr->binaryop.src1].getint() + - registers[instr->binaryop.src2].getint()); + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setint(lhs.getint() + rhs.getint()); + } break; case Instr::AddString: - new (registers[instr->binaryop.output].getstringptr()) - QString(*registers[instr->binaryop.src1].getstringptr() + - *registers[instr->binaryop.src2].getstringptr()); + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() && rhs.isUndefined()) { output.setNaN(); } + else { + if (lhs.isUndefined()) + new (output.getstringptr()) + QString(QLatin1String("undefined") + *registers[instr->binaryop.src2].getstringptr()); + else if (rhs.isUndefined()) + new (output.getstringptr()) + QString(*registers[instr->binaryop.src1].getstringptr() + QLatin1String("undefined")); + else + new (output.getstringptr()) + QString(*registers[instr->binaryop.src1].getstringptr() + + *registers[instr->binaryop.src2].getstringptr()); + output.settype(QMetaType::QString); + } + } break; case Instr::MinusReal: - registers[instr->binaryop.output].setqreal(registers[instr->binaryop.src1].getqreal() - - registers[instr->binaryop.src2].getqreal()); + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setqreal(lhs.getqreal() - rhs.getqreal()); + } break; case Instr::MinusInt: - registers[instr->binaryop.output].setint(registers[instr->binaryop.src1].getint() - - registers[instr->binaryop.src2].getint()); + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setint(lhs.getint() - rhs.getint()); + } break; case Instr::CompareReal: - registers[instr->binaryop.output].setbool(registers[instr->binaryop.src1].getqreal() == - registers[instr->binaryop.src2].getqreal()); + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() == rhs.isUndefined()); + else output.setbool(lhs.getqreal() == rhs.getqreal()); + } break; case Instr::CompareString: - registers[instr->binaryop.output].setbool(*registers[instr->binaryop.src1].getstringptr() == - *registers[instr->binaryop.src2].getstringptr()); + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() == rhs.isUndefined()); + else output.setbool(*lhs.getstringptr() == *rhs.getstringptr()); + } break; case Instr::NotCompareReal: - registers[instr->binaryop.output].setbool(registers[instr->binaryop.src1].getqreal() != - registers[instr->binaryop.src2].getqreal()); + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() != rhs.isUndefined()); + else output.setbool(lhs.getqreal() != rhs.getqreal()); + } break; case Instr::NotCompareString: - registers[instr->binaryop.output].setbool(*registers[instr->binaryop.src1].getstringptr() != - *registers[instr->binaryop.src2].getstringptr()); + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(lhs.isUndefined() != rhs.isUndefined()); + else output.setbool(*lhs.getstringptr() != *rhs.getstringptr()); + } break; case Instr::GreaterThanReal: - registers[instr->binaryop.output].setbool(registers[instr->binaryop.src1].getqreal() > - registers[instr->binaryop.src2].getqreal()); + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setbool(false); + else output.setbool(lhs.getqreal() > rhs.getqreal()); + } break; + case Instr::MaxReal: - registers[instr->binaryop.output].setqreal(qMax(registers[instr->binaryop.src1].getqreal(), - registers[instr->binaryop.src2].getqreal())); + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setqreal(qMax(lhs.getqreal(), rhs.getqreal())); + } break; + case Instr::MinReal: - registers[instr->binaryop.output].setqreal(qMin(registers[instr->binaryop.src1].getqreal(), - registers[instr->binaryop.src2].getqreal())); + { + const Register &lhs = registers[instr->binaryop.src1]; + const Register &rhs = registers[instr->binaryop.src2]; + Register &output = registers[instr->binaryop.output]; + if (lhs.isUndefined() || rhs.isUndefined()) output.setNaN(); + else output.setqreal(qMin(lhs.getqreal(), rhs.getqreal())); + } break; + case Instr::NewString: - new (registers[instr->construct.reg].typeMemory()) QString; + { + Register &output = registers[instr->construct.reg]; + new (output.getstringptr()) QString; + output.settype(QMetaType::QString); + } break; case Instr::NewUrl: - new (registers[instr->construct.reg].typeMemory()) QUrl; + { + Register &output = registers[instr->construct.reg]; + new (output.geturlptr()) QUrl; + output.settype(QMetaType::QUrl); + } break; case Instr::CleanupString: @@ -830,25 +975,38 @@ void QmlBindingVME::run(const char *programData, int instrIndex, 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 }; + { + const Register &input = registers[instr->fetch.objectReg]; + Register &output = registers[instr->fetch.output]; + + if (input.isUndefined()) { + throwException(instr->fetch.exceptionId, error, program, context); + return; + } + + QObject *object = input.getQObject(); + if (!object) { + output.setUndefined(); + } else { + void *argv[] = { output.typeDataPtr(), 0 }; QMetaObject::metacall(object, QMetaObject::ReadProperty, instr->fetch.index, argv); } + } break; case Instr::Store: - { - 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); + { + Register &data = registers[instr->store.reg]; + if (data.isUndefined()) { + throwException(instr->store.exceptionId, error, program, context); + return; } + + int status = -1; + void *argv[] = { data.typeDataPtr(), 0, &status, &storeFlags }; + QMetaObject::metacall(output, QMetaObject::WriteProperty, + instr->store.index, argv); + } break; case Instr::Copy: @@ -880,68 +1038,81 @@ void QmlBindingVME::run(const char *programData, int instrIndex, // 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; - } + findgeneric(registers + instr->find.reg, config, instr->find.subscribeIndex, + QmlContextPrivate::get(context->parent), + config->identifiers[instr->find.name].identifier, + instr->common.type == Instr::FindGenericTerminal); break; case Instr::FindPropertyTerminal: case Instr::FindProperty: - 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*"; + { + const Register &object = registers[instr->find.src]; + if (object.isUndefined()) { + throwException(instr->find.exceptionId, error, program, context); return; } + + findproperty(object.getQObject(), registers + instr->find.reg, + QmlEnginePrivate::get(context->engine), config, + instr->find.subscribeIndex, config->identifiers[instr->find.name].identifier, + instr->common.type == Instr::FindPropertyTerminal); + } break; case Instr::CleanupGeneric: - { - int type = registers[instr->cleanup.reg].gettype(); - if (type == qMetaTypeId<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(); - } + { + 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)); - } + { + Register &output = registers[instr->unaryop.output]; + Register &input = registers[instr->unaryop.src]; + bool ok = true; + output.setqreal(toReal(&input, input.gettype(), &ok)); + if (!ok) output.setUndefined(); + } break; case Instr::ConvertGenericToBool: - { - int type = registers[instr->unaryop.src].gettype(); - registers[instr->unaryop.output].setbool(toBool(registers + instr->unaryop.src, type)); - } + { + Register &output = registers[instr->unaryop.output]; + Register &input = registers[instr->unaryop.src]; + bool ok = true; + output.setbool(toBool(&input, input.gettype(), &ok)); + if (!ok) output.setUndefined(); + } break; case Instr::ConvertGenericToString: - { - int type = registers[instr->unaryop.src].gettype(); - void *regPtr = registers[instr->unaryop.output].typeDataPtr(); - new (regPtr) QString(toString(registers + instr->unaryop.src, type)); - } + { + Register &output = registers[instr->unaryop.output]; + Register &input = registers[instr->unaryop.src]; + bool ok = true; + QString str = toString(&input, input.gettype(), &ok); + if (ok) { new (output.getstringptr()) QString(str); output.settype(QMetaType::QString); } + else { output.setUndefined(); } + } break; case Instr::ConvertGenericToUrl: - { - int type = registers[instr->unaryop.src].gettype(); - void *regPtr = registers[instr->unaryop.output].typeDataPtr(); - new (regPtr) QUrl(toUrl(registers + instr->unaryop.src, type, context)); - } + { + Register &output = registers[instr->unaryop.output]; + Register &input = registers[instr->unaryop.src]; + bool ok = true; + QUrl url = toUrl(&input, input.gettype(), context, &ok); + if (ok) { new (output.geturlptr()) QUrl(url); output.settype(QMetaType::QUrl); } + else { output.setUndefined(); } + } break; default: @@ -1118,6 +1289,7 @@ void QmlBindingCompilerPrivate::resetInstanceState() registers = 0; registerCleanups.clear(); data = committed.data; + exceptions = committed.exceptions; usedSubscriptionIds.clear(); subscriptionSet.clear(); subscriptionIds = committed.subscriptionIds; @@ -1138,6 +1310,7 @@ int QmlBindingCompilerPrivate::commitCompile() committed.dependencies << usedSubscriptionIds; committed.bytecode << bytecode; committed.data = data; + committed.exceptions = exceptions; committed.subscriptionIds = subscriptionIds; committed.registeredStrings = registeredStrings; return rv; @@ -1201,6 +1374,7 @@ bool QmlBindingCompilerPrivate::compile(QmlJS::AST::Node *node) instr.store.output = 0; instr.store.index = destination->index; instr.store.reg = convertReg; + instr.store.exceptionId = 0xFF; bytecode << instr; if (destination->type == QVariant::String) { @@ -1302,7 +1476,8 @@ bool QmlBindingCompilerPrivate::tryName(QmlJS::AST::Node *node) bool QmlBindingCompilerPrivate::parseName(AST::Node *node, Result &type) { QStringList nameParts; - if (!buildName(nameParts, node)) + QList<AST::ExpressionNode *> nameNodes; + if (!buildName(nameParts, node, &nameNodes)) return false; int reg = acquireReg(); @@ -1355,6 +1530,7 @@ bool QmlBindingCompilerPrivate::parseName(AST::Node *node, Result &type) attach.attached.output = reg; attach.attached.reg = reg; attach.attached.index = attachType->index(); + attach.attached.exceptionId = exceptionId(nameNodes.at(ii)); bytecode << attach; subscribeName << contextName(); @@ -1422,7 +1598,7 @@ bool QmlBindingCompilerPrivate::parseName(AST::Node *node, Result &type) subscribeName << contextName(); subscribeName << name; - fetch(type, context->metaObject(), reg, d0Idx, subscribeName); + fetch(type, context->metaObject(), reg, d0Idx, subscribeName, nameNodes.at(ii)); } else if(d1Idx != -1) { Instr instr; instr.common.type = Instr::LoadRoot; @@ -1433,7 +1609,7 @@ bool QmlBindingCompilerPrivate::parseName(AST::Node *node, Result &type) subscribeName << QLatin1String("$$$ROOT"); subscribeName << name; - fetch(type, component->metaObject(), reg, d1Idx, subscribeName); + fetch(type, component->metaObject(), reg, d1Idx, subscribeName, nameNodes.at(ii)); } else { Instr find; if (nameParts.count() == 1) @@ -1444,6 +1620,7 @@ bool QmlBindingCompilerPrivate::parseName(AST::Node *node, Result &type) 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)) @@ -1492,7 +1669,7 @@ bool QmlBindingCompilerPrivate::parseName(AST::Node *node, Result &type) if (absType || (wasAttachedObject && idx != -1) || (mo && mo->property(idx).isFinal())) { absType = 0; - fetch(type, mo, reg, idx, subscribeName); + fetch(type, mo, reg, idx, subscribeName, nameNodes.at(ii)); if (type.type == -1) return false; } else { @@ -1506,6 +1683,8 @@ bool QmlBindingCompilerPrivate::parseName(AST::Node *node, Result &type) 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 @@ -1928,18 +2107,21 @@ bool QmlBindingCompilerPrivate::parseMethod(QmlJS::AST::Node *node, Result &resu } bool QmlBindingCompilerPrivate::buildName(QStringList &name, - QmlJS::AST::Node *node) + QmlJS::AST::Node *node, + QList<QmlJS::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)) + if (!buildName(name, expr->base, nodes)) return false; name << expr->name->asString(); + if (nodes) *nodes << expr; } else { return false; } @@ -1948,7 +2130,8 @@ bool QmlBindingCompilerPrivate::buildName(QStringList &name, } -bool QmlBindingCompilerPrivate::fetch(Result &rv, const QMetaObject *mo, int reg, int idx, const QStringList &subName) +bool QmlBindingCompilerPrivate::fetch(Result &rv, const QMetaObject *mo, int reg, + int idx, const QStringList &subName, QmlJS::AST::ExpressionNode *node) { QMetaProperty prop = mo->property(idx); rv.metaObject = 0; @@ -1968,6 +2151,7 @@ bool QmlBindingCompilerPrivate::fetch(Result &rv, const QMetaObject *mo, int reg fetch.fetch.objectReg = reg; fetch.fetch.index = idx; fetch.fetch.output = reg; + fetch.fetch.exceptionId = exceptionId(node); rv.type = prop.userType(); rv.metaObject = QmlMetaType::metaObjectForType(rv.type); @@ -2130,6 +2314,19 @@ bool QmlBindingCompilerPrivate::subscriptionNeutral(const QSet<QString> &base, return difflhs.isEmpty(); } +quint8 QmlBindingCompilerPrivate::exceptionId(QmlJS::AST::ExpressionNode *n) +{ + quint8 rv = 0xFF; + if (exceptions.count() < 0xFF) { + rv = (quint8)exceptions.count(); + QmlJS::AST::SourceLocation l = n->firstSourceLocation(); + quint64 e = l.startLine; + e <<= 32; + e |= l.startColumn; + exceptions.append(e); + } + return rv; +} QmlBindingCompiler::QmlBindingCompiler() : d(new QmlBindingCompilerPrivate) @@ -2196,6 +2393,14 @@ QByteArray QmlBindingCompilerPrivate::buildSignalTable() const return QByteArray((const char *)header.constData(), header.count() * sizeof(quint32)); } +QByteArray QmlBindingCompilerPrivate::buildExceptionData() const +{ + QByteArray rv; + rv.resize(committed.exceptions.count() * sizeof(quint64)); + ::memcpy(rv.data(), committed.exceptions.constData(), rv.size()); + return rv; +} + /* Returns the compiled program. */ @@ -2222,6 +2427,9 @@ QByteArray QmlBindingCompiler::program() const 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(); diff --git a/src/declarative/qml/qmlbindingvme_p.h b/src/declarative/qml/qmlbindingvme_p.h index 4ef7d04..629a979 100644 --- a/src/declarative/qml/qmlbindingvme_p.h +++ b/src/declarative/qml/qmlbindingvme_p.h @@ -84,8 +84,8 @@ public: static void init(const char *program, Config *config, quint32 **sigTable, quint32 *bindingCount); static void run(const char *program, int instr, - Config *config, QmlContextPrivate *context, - QObject **scopes, QObject **outputs); + Config *config, QmlContextPrivate *context, QmlDelayedError *error, + QObject *scope, QObject *output); static void dump(const char *); }; diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index 13c1afb..85e8c80 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -99,7 +99,7 @@ class QmlTypeNameCache; class QmlComponentAttached; class QmlListScriptClass; class QmlCleanup; -class QmlBindingData; +class QmlDelayedError; class QmlWorkerScriptEngine; class QmlGlobalScriptClass; @@ -167,7 +167,7 @@ public: QmlCleanup *cleanup; // Bindings that have had errors during startup - QmlBindingData *erroredBindings; + QmlDelayedError *erroredBindings; int inProgressCreations; QmlScriptEngine scriptEngine; diff --git a/src/declarative/qml/qmlexpression.cpp b/src/declarative/qml/qmlexpression.cpp index c00e552..dcc9c30 100644 --- a/src/declarative/qml/qmlexpression.cpp +++ b/src/declarative/qml/qmlexpression.cpp @@ -56,6 +56,20 @@ Q_DECLARE_METATYPE(QList<QObject *>); QT_BEGIN_NAMESPACE +bool QmlDelayedError::addError(QmlEnginePrivate *e) +{ + if (!e || prevError) return false; + + if (e->inProgressCreations == 0) return false; // Not in construction + + prevError = &e->erroredBindings; + nextError = e->erroredBindings; + e->erroredBindings = this; + if (nextError) nextError->prevError = &nextError; + + return true; +} + QmlExpressionData::QmlExpressionData() : expressionFunctionValid(false), expressionRewritten(false), me(0), trackChange(true), isShared(false), line(-1), guardList(0), guardListLength(0) diff --git a/src/declarative/qml/qmlexpression_p.h b/src/declarative/qml/qmlexpression_p.h index 8561a57..5be93f0 100644 --- a/src/declarative/qml/qmlexpression_p.h +++ b/src/declarative/qml/qmlexpression_p.h @@ -84,7 +84,30 @@ private: QmlAbstractExpression *m_nextExpression; }; -class QmlExpressionData : public QmlAbstractExpression, public QmlRefCount +class QmlDelayedError +{ +public: + inline QmlDelayedError() : nextError(0), prevError(0) {} + inline ~QmlDelayedError() { removeError(); } + + QmlError error; + + bool addError(QmlEnginePrivate *); + + inline void removeError() { + if (!prevError) return; + if (nextError) nextError->prevError = prevError; + *prevError = nextError; + nextError = 0; + prevError = 0; + } + +private: + QmlDelayedError *nextError; + QmlDelayedError **prevError; +}; + +class QmlExpressionData : public QmlAbstractExpression, public QmlDelayedError, public QmlRefCount { public: QmlExpressionData(); @@ -97,8 +120,6 @@ public: bool expressionRewritten:1; QScriptValue expressionFunction; - QmlError error; - QmlBasicScript sse; QObject *me; bool trackChange; |