aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/types/qqmllistmodel.cpp
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2014-04-25 16:19:02 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-06-04 08:25:52 +0200
commit52e07d564b65ed6ce26955a676c7692ad67686c1 (patch)
tree04354cdf1d56776e9b52819f4329b70c57e235dc /src/qml/types/qqmllistmodel.cpp
parent481447ae664fa2998cb03f93f0c066caa2782bf0 (diff)
Rework custom parser integration
The custom parser design used to be so that the custom parser operates on the "AST", creates its own binary representation of the data it needs, stores it in a QByteArray and gets that at object instantiation time. That meant serializing everything necessary. With the introduction of the "binary" QML data structure, that process of serialization becomes obsolete and would require extra work in the custom parsers for example for QQuickStates to store the translation parameters. The clean solution is to eliminate this unnecessary serialization process and instead let the custom parsers do a verification pass at type compile time and then simply operate directly on the QV4::CompiledData::Bindings at object instantiation time. That simplifies the code, and allows for support of translations throughout all list model properties. Additionally this speeds up the creation of state objects and reduces memory consumption. Previously a text: qsTr("foo") binding in states would result in an actual java script binding. After this patch it is merely stored as a string and translated at object instantiation time. Change-Id: I7550274513f54abb09a0ab4de51c4c0bcdb23cae Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/qml/types/qqmllistmodel.cpp')
-rw-r--r--src/qml/types/qqmllistmodel.cpp240
1 files changed, 68 insertions, 172 deletions
diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp
index 0056276d52..1b074efd56 100644
--- a/src/qml/types/qqmllistmodel.cpp
+++ b/src/qml/types/qqmllistmodel.cpp
@@ -44,7 +44,7 @@
#include <private/qqmlopenmetaobject_p.h>
#include <private/qqmljsast_p.h>
#include <private/qqmljsengine_p.h>
-
+#include <private/qqmlcompiler_p.h>
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlengine_p.h>
@@ -1430,11 +1430,6 @@ void DynamicRoleModelNodeMetaObject::propertyWritten(int index)
}
}
-QQmlListModelParser::ListInstruction *QQmlListModelParser::ListModelData::instructions() const
-{
- return (QQmlListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData));
-}
-
/*!
\qmltype ListModel
\instantiates QQmlListModel
@@ -2254,7 +2249,7 @@ void QQmlListModel::sync()
qmlInfo(this) << "List sync() can only be called from a WorkerScript";
}
-bool QQmlListModelParser::compileProperty(const QV4::CompiledData::QmlUnit *qmlUnit, const QV4::CompiledData::Binding *binding, QList<QQmlListModelParser::ListInstruction> &instr, QByteArray &data)
+bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::QmlUnit *qmlUnit, const QV4::CompiledData::Binding *binding)
{
if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
const quint32 targetObjectIndex = binding->value.objectIndex;
@@ -2269,13 +2264,6 @@ bool QQmlListModelParser::compileProperty(const QV4::CompiledData::QmlUnit *qmlU
listElementTypeName = objName; // cache right name for next time
}
- {
- ListInstruction li;
- li.type = ListInstruction::Push;
- li.dataIdx = -1;
- instr << li;
- }
-
if (!qmlUnit->header.stringAt(target->idIndex).isEmpty()) {
error(target->locationOfIdProperty, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property"));
return false;
@@ -2288,208 +2276,116 @@ bool QQmlListModelParser::compileProperty(const QV4::CompiledData::QmlUnit *qmlU
error(binding, QQmlListModel::tr("ListElement: cannot contain nested elements"));
return false;
}
- ListInstruction li;
- int ref = data.count();
- data.append(propName.toUtf8());
- data.append('\0');
- li.type = ListInstruction::Set;
- li.dataIdx = ref;
- instr << li;
-
- if (!compileProperty(qmlUnit, binding, instr, data))
+ if (!verifyProperty(qmlUnit, binding))
+ return false;
+ }
+ } else if (binding->type == QV4::CompiledData::Binding::Type_Script) {
+ QString scriptStr = binding->valueAsScriptString(&qmlUnit->header);
+ if (!definesEmptyList(scriptStr)) {
+ QByteArray script = scriptStr.toUtf8();
+ bool ok;
+ evaluateEnum(script, &ok);
+ if (!ok) {
+ error(binding, QQmlListModel::tr("ListElement: cannot use script for property value"));
return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool QQmlListModelParser::applyProperty(const QV4::CompiledData::QmlUnit *qmlUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex)
+{
+ const QString elementName = qmlUnit->header.stringAt(binding->propertyNameIndex);
- li.type = ListInstruction::Pop;
- li.dataIdx = -1;
- instr << li;
+ bool roleSet = false;
+ if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
+ const quint32 targetObjectIndex = binding->value.objectIndex;
+ const QV4::CompiledData::Object *target = qmlUnit->objectAt(targetObjectIndex);
+
+ ListModel *subModel = 0;
+ if (outterElementIndex == -1) {
+ subModel = model;
+ } else {
+ const ListLayout::Role &role = model->getOrCreateListRole(elementName);
+ if (role.type == ListLayout::Role::List) {
+ subModel = model->getListProperty(outterElementIndex, role);
+ if (subModel == 0) {
+ subModel = new ListModel(role.subLayout, 0, -1);
+ QVariant vModel = QVariant::fromValue(subModel);
+ model->setOrCreateProperty(outterElementIndex, elementName, vModel);
+ }
+ }
}
- {
- ListInstruction li;
- li.type = ListInstruction::Pop;
- li.dataIdx = -1;
- instr << li;
+ int elementIndex = subModel ? subModel->appendElement() : -1;
+
+ const QV4::CompiledData::Binding *subBinding = target->bindingTable();
+ for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) {
+ roleSet |= applyProperty(qmlUnit, subBinding, subModel, elementIndex);
}
} else {
- int ref = data.count();
-
- QByteArray d;
+ QVariant value;
- if (binding->type == QV4::CompiledData::Binding::Type_String) {
- d += char(String);
- d += binding->valueAsString(&qmlUnit->header).toUtf8();
+ if (binding->evaluatesToString()) {
+ value = binding->valueAsString(&qmlUnit->header);
} else if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- d += char(Number);
- d += QByteArray::number(binding->valueAsNumber(),'g',20);
+ value = binding->valueAsNumber();
} else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
- d += char(Boolean);
- d += char(binding->valueAsBoolean());
- } else if (binding->type == QV4::CompiledData::Binding::Type_Translation
- || binding->type == QV4::CompiledData::Binding::Type_TranslationById) {
- error(binding, QQmlListModel::tr("ListElement: cannot use script for property value"));
- return false;
+ value = binding->valueAsBoolean();
} else if (binding->type == QV4::CompiledData::Binding::Type_Script) {
QString scriptStr = binding->valueAsScriptString(&qmlUnit->header);
if (definesEmptyList(scriptStr)) {
- d[0] = char(Invalid); // marks empty list
+ const ListLayout::Role &role = model->getOrCreateListRole(elementName);
+ ListModel *emptyModel = new ListModel(role.subLayout, 0, -1);
+ value = QVariant::fromValue(emptyModel);
} else {
QByteArray script = scriptStr.toUtf8();
bool ok;
- int v = evaluateEnum(script, &ok);
- if (!ok) {
- error(binding, QQmlListModel::tr("ListElement: cannot use script for property value"));
- return false;
- } else {
- d[0] = char(Number);
- d += QByteArray::number(v);
- }
+ value = evaluateEnum(script, &ok);
}
} else {
Q_UNREACHABLE();
}
- d.append('\0');
- data.append(d);
-
- ListInstruction li;
- li.type = ListInstruction::Value;
- li.dataIdx = ref;
- instr << li;
+ model->setOrCreateProperty(outterElementIndex, elementName, value);
+ roleSet = true;
}
-
- return true;
+ return roleSet;
}
-QByteArray QQmlListModelParser::compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+void QQmlListModelParser::verifyBindings(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
- QList<ListInstruction> instr;
- QByteArray data;
listElementTypeName = QString(); // unknown
foreach (const QV4::CompiledData::Binding *binding, bindings) {
QString propName = qmlUnit->header.stringAt(binding->propertyNameIndex);
if (!propName.isEmpty()) { // isn't default property
error(binding, QQmlListModel::tr("ListModel: undefined property '%1'").arg(propName));
- return QByteArray();
+ return;
}
- if (!compileProperty(qmlUnit, binding, instr, data))
- return QByteArray();
+ if (!verifyProperty(qmlUnit, binding))
+ return;
}
-
- int size = sizeof(ListModelData) +
- instr.count() * sizeof(ListInstruction) +
- data.count();
-
- QByteArray rv;
- rv.resize(size);
-
- ListModelData *lmd = (ListModelData *)rv.data();
- lmd->dataOffset = sizeof(ListModelData) +
- instr.count() * sizeof(ListInstruction);
- lmd->instrCount = instr.count();
- for (int ii = 0; ii < instr.count(); ++ii)
- lmd->instructions()[ii] = instr.at(ii);
- ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count());
-
- return rv;
}
-void QQmlListModelParser::setCustomData(QObject *obj, const QByteArray &d, QQmlCompiledData *)
+void QQmlListModelParser::applyBindings(QObject *obj, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings)
{
QQmlListModel *rv = static_cast<QQmlListModel *>(obj);
QV8Engine *engine = QQmlEnginePrivate::getV8Engine(qmlEngine(rv));
rv->m_engine = engine;
- const ListModelData *lmd = (const ListModelData *)d.constData();
- const char *data = ((const char *)lmd) + lmd->dataOffset;
+ const QV4::CompiledData::QmlUnit *qmlUnit = cdata->qmlUnit;
bool setRoles = false;
- QStack<DataStackElement> stack;
-
- for (int ii = 0; ii < lmd->instrCount; ++ii) {
- const ListInstruction &instr = lmd->instructions()[ii];
-
- switch(instr.type) {
- case ListInstruction::Push:
- {
- Q_ASSERT(!rv->m_dynamicRoles);
-
- ListModel *subModel = 0;
-
- if (stack.count() == 0) {
- subModel = rv->m_listModel;
- } else {
- const DataStackElement &e0 = stack.at(stack.size() - 1);
- DataStackElement &e1 = stack[stack.size() - 2];
-
- const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name);
- if (role.type == ListLayout::Role::List) {
- subModel = e1.model->getListProperty(e1.elementIndex, role);
-
- if (subModel == 0) {
- subModel = new ListModel(role.subLayout, 0, -1);
- QVariant vModel = QVariant::fromValue(subModel);
- e1.model->setOrCreateProperty(e1.elementIndex, e0.name, vModel);
- }
- }
- }
-
- DataStackElement e;
- e.model = subModel;
- e.elementIndex = subModel ? subModel->appendElement() : -1;
- stack.push(e);
- }
- break;
-
- case ListInstruction::Pop:
- stack.pop();
- break;
-
- case ListInstruction::Value:
- {
- const DataStackElement &e0 = stack.at(stack.size() - 1);
- DataStackElement &e1 = stack[stack.size() - 2];
-
- QString name = e0.name;
- QVariant value;
-
- switch (PropertyType(data[instr.dataIdx])) {
- case Invalid:
- {
- const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name);
- ListModel *emptyModel = new ListModel(role.subLayout, 0, -1);
- value = QVariant::fromValue(emptyModel);
- }
- break;
- case Boolean:
- value = bool(data[1 + instr.dataIdx]);
- break;
- case Number:
- value = QByteArray(data + 1 + instr.dataIdx).toDouble();
- break;
- case String:
- value = QString::fromUtf8(data + 1 + instr.dataIdx);
- break;
- default:
- Q_ASSERT("Format error in ListInstruction");
- }
-
- e1.model->setOrCreateProperty(e1.elementIndex, name, value);
- setRoles = true;
- }
- break;
-
- case ListInstruction::Set:
- {
- DataStackElement e;
- e.name = QString::fromUtf8(data + instr.dataIdx);
- stack.push(e);
- }
- break;
- }
+ foreach (const QV4::CompiledData::Binding *binding, bindings) {
+ if (binding->type != QV4::CompiledData::Binding::Type_Object)
+ continue;
+ setRoles |= applyProperty(qmlUnit, binding, rv->m_listModel, /*outter element index*/-1);
}
if (setRoles == false)