diff options
author | Simon Hausmann <simon.hausmann@digia.com> | 2014-01-15 17:29:04 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-01-20 15:10:00 +0100 |
commit | ae0a2ea25714af603babe5aa0de364d1ebae1170 (patch) | |
tree | 91a0845dd397af79b30341aae64f6c24f0f43197 /src/qml/types | |
parent | f8176d72ac491469ccf0288cbb86c0ccc12fb47b (diff) |
[new compiler] Add support for QML list models
List model definitions make heavy use of custom parsers, which requires AST
access as well as a general port to the new QQmlCustomParser API.
Additional fixes in the custom parser support were needed to pass all tests:
* Fix support for AcceptsSignalHandlers and AcceptsAttachedProperties
* Don't call setCustomData unless the compiler generated data earlier
Change-Id: Ic42f8a890391267c94f63d35f055b60fdbf3c83d
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/qml/types')
-rw-r--r-- | src/qml/types/qqmllistmodel.cpp | 164 | ||||
-rw-r--r-- | src/qml/types/qqmllistmodel_p.h | 2 |
2 files changed, 166 insertions, 0 deletions
diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index eeb4aa0861..03dfb6690f 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -2384,6 +2384,136 @@ bool QQmlListModelParser::compileProperty(const QQmlCustomParserProperty &prop, return true; } +bool QQmlListModelParser::compileProperty(const QV4::CompiledData::QmlUnit *qmlUnit, const QV4::CompiledData::Binding *binding, QList<QQmlListModelParser::ListInstruction> &instr, QByteArray &data) +{ + if (binding->type >= QV4::CompiledData::Binding::Type_Object) { + const QV4::CompiledData::Object *target = qmlUnit->objectAt(binding->value.objectIndex); + QString objName = qmlUnit->header.stringAt(target->inheritedTypeNameIndex); + if (objName != listElementTypeName) { + const QMetaObject *mo = resolveType(objName); + if (mo != &QQmlListElement::staticMetaObject) { + error(target, QQmlListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + 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(binding, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property")); + return false; + } + + const QV4::CompiledData::Binding *binding = target->bindingTable(); + for (quint32 i = 0; i < target->nBindings; ++i, ++binding) { + QString propName = qmlUnit->header.stringAt(binding->propertyNameIndex); + if (propName.isEmpty()) { + 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)) + return false; + + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + { + ListInstruction li; + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + } else { + int ref = data.count(); + + QByteArray d; + + if (binding->type == QV4::CompiledData::Binding::Type_String) { + d += char(QQmlScript::Variant::String); + d += binding->valueAsString(&qmlUnit->header).toUtf8(); + } else if (binding->type == QV4::CompiledData::Binding::Type_Number) { + d += char(QQmlScript::Variant::Number); + d += QByteArray::number(binding->valueAsNumber(),'g',20); + } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { + d += char(QQmlScript::Variant::Boolean); + d += char(binding->valueAsBoolean()); + } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { + QString scriptStr = binding->valueAsScriptString(&qmlUnit->header); + if (definesEmptyList(scriptStr)) { + d[0] = char(QQmlScript::Variant::Invalid); // marks empty list + } else { + QByteArray script = scriptStr.toUtf8(); + bool ok; + int v = evaluateEnum(script, &ok); + if (!ok) { + using namespace QQmlJS; + AST::Node *node = astForBinding(binding->value.compiledScriptIndex); + if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement*>(node)) + node = stmt->expression; + AST::StringLiteral *literal = 0; + if (AST::CallExpression *callExpr = AST::cast<AST::CallExpression *>(node)) { + if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(callExpr->base)) { + if (idExpr->name == QLatin1String("QT_TR_NOOP") || idExpr->name == QLatin1String("QT_TRID_NOOP")) { + if (callExpr->arguments && !callExpr->arguments->next) + literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->expression); + if (!literal) { + error(binding, QQmlListModel::tr("ListElement: improperly specified %1").arg(idExpr->name.toString())); + return false; + } + } else if (idExpr->name == QLatin1String("QT_TRANSLATE_NOOP")) { + if (callExpr->arguments && callExpr->arguments->next && !callExpr->arguments->next->next) + literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->next->expression); + if (!literal) { + error(binding, QQmlListModel::tr("ListElement: improperly specified QT_TRANSLATE_NOOP")); + return false; + } + } + } + } + + if (literal) { + d[0] = char(QQmlScript::Variant::String); + d += literal->value.toUtf8(); + } else { + error(binding, QQmlListModel::tr("ListElement: cannot use script for property value")); + return false; + } + } else { + d[0] = char(QQmlScript::Variant::Number); + d += QByteArray::number(v); + } + } + } + + d.append('\0'); + data.append(d); + + ListInstruction li; + li.type = ListInstruction::Value; + li.dataIdx = ref; + instr << li; + } + + return true; +} + QByteArray QQmlListModelParser::compile(const QList<QQmlCustomParserProperty> &customProps) { QList<ListInstruction> instr; @@ -2420,6 +2550,40 @@ QByteArray QQmlListModelParser::compile(const QList<QQmlCustomParserProperty> &c return rv; } +QByteArray QQmlListModelParser::compile(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(); + } + if (!compileProperty(qmlUnit, binding, instr, data)) + return QByteArray(); + } + + 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) { QQmlListModel *rv = static_cast<QQmlListModel *>(obj); diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h index a7487537c5..172d857687 100644 --- a/src/qml/types/qqmllistmodel_p.h +++ b/src/qml/types/qqmllistmodel_p.h @@ -160,6 +160,7 @@ class QQmlListModelParser : public QQmlCustomParser public: QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} QByteArray compile(const QList<QQmlCustomParserProperty> &); + QByteArray compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings); void setCustomData(QObject *, const QByteArray &); private: @@ -175,6 +176,7 @@ private: ListInstruction *instructions() const; }; bool compileProperty(const QQmlCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data); + bool compileProperty(const QV4::CompiledData::QmlUnit *qmlUnit, const QV4::CompiledData::Binding *binding, QList<ListInstruction> &instr, QByteArray &data); bool definesEmptyList(const QString &); |