summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Kennedy <aaron.kennedy@nokia.com>2010-01-22 05:37:39 (GMT)
committerAaron Kennedy <aaron.kennedy@nokia.com>2010-01-22 08:01:48 (GMT)
commit3a75eeb5da34fbb50173c3f610c08d160c21f4e3 (patch)
treeab854f509d3a3720b74d2ef9d5e7803881078673
parent0fe084df052b8ac66918d0b2732b65f5569881c0 (diff)
downloadQt-3a75eeb5da34fbb50173c3f610c08d160c21f4e3.zip
Qt-3a75eeb5da34fbb50173c3f610c08d160c21f4e3.tar.gz
Qt-3a75eeb5da34fbb50173c3f610c08d160c21f4e3.tar.bz2
Harden QML binding optimizer
-rw-r--r--src/declarative/qml/qmlbinding.cpp37
-rw-r--r--src/declarative/qml/qmlbinding_p.h4
-rw-r--r--src/declarative/qml/qmlbindingoptimizations.cpp8
-rw-r--r--src/declarative/qml/qmlbindingoptimizations_p.h2
-rw-r--r--src/declarative/qml/qmlbindingvme.cpp668
-rw-r--r--src/declarative/qml/qmlbindingvme_p.h4
-rw-r--r--src/declarative/qml/qmlengine_p.h4
-rw-r--r--src/declarative/qml/qmlexpression.cpp14
-rw-r--r--src/declarative/qml/qmlexpression_p.h27
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;