aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/util/qquickpropertychanges.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/quick/util/qquickpropertychanges.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/quick/util/qquickpropertychanges.cpp')
-rw-r--r--src/quick/util/qquickpropertychanges.cpp193
1 files changed, 88 insertions, 105 deletions
diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp
index 1a631703fb..5b5b8cac3c 100644
--- a/src/quick/util/qquickpropertychanges.cpp
+++ b/src/quick/util/qquickpropertychanges.cpp
@@ -204,7 +204,7 @@ public:
isExplicit(false) {}
QPointer<QObject> object;
- QByteArray data;
+ QList<const QV4::CompiledData::Binding *> bindings;
QQmlRefPointer<QQmlCompiledData> cdata;
bool decoded : 1;
@@ -212,6 +212,7 @@ public:
bool isExplicit : 1;
void decode();
+ void decodeBinding(const QString &propertyPrefix, const QV4::CompiledData::QmlUnit *qmlUnit, const QV4::CompiledData::Binding *binding);
class ExpressionChange {
public:
@@ -237,10 +238,8 @@ public:
QQmlProperty property(const QString &);
};
-void QQuickPropertyChangesParser::compileList(QList<QPair<QString, const QV4::CompiledData::Binding*> > &list, const QString &pre, const QV4::CompiledData::QmlUnit *qmlUnit, const QV4::CompiledData::Binding *binding)
+void QQuickPropertyChangesParser::verifyList(const QV4::CompiledData::QmlUnit *qmlUnit, const QV4::CompiledData::Binding *binding)
{
- QString propName = pre + qmlUnit->header.stringAt(binding->propertyNameIndex);
-
if (binding->type == QV4::CompiledData::Binding::Type_Object) {
error(qmlUnit->objectAt(binding->value.objectIndex), QQuickPropertyChanges::tr("PropertyChanges does not support creating state-specific objects."));
return;
@@ -248,129 +247,105 @@ void QQuickPropertyChangesParser::compileList(QList<QPair<QString, const QV4::Co
if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty
|| binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
- QString pre = propName + QLatin1Char('.');
const QV4::CompiledData::Object *subObj = qmlUnit->objectAt(binding->value.objectIndex);
const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) {
- compileList(list, pre, qmlUnit, subBinding);
+ verifyList(qmlUnit, subBinding);
}
- return;
}
-
- list << qMakePair(propName, binding);
}
-QByteArray QQuickPropertyChangesParser::compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props)
+void QQuickPropertyChangesPrivate::decode()
{
- QList<QPair<QString, const QV4::CompiledData::Binding *> > data;
- for (int ii = 0; ii < props.count(); ++ii)
- compileList(data, QString(), qmlUnit, props.at(ii));
-
- QByteArray rv;
- QDataStream ds(&rv, QIODevice::WriteOnly);
-
- ds << data.count();
- for (int ii = 0; ii < data.count(); ++ii) {
- const QV4::CompiledData::Binding *binding = data.at(ii).second;
- ds << data.at(ii).first << int(binding->type);
- QVariant var;
- switch (binding->type) {
- case QV4::CompiledData::Binding::Type_Script:
- ds << bindingIdentifier(binding);
- // Fall through as we also need the expression string.
- // Signal handlers still need to be constructed by string ;(
- case QV4::CompiledData::Binding::Type_String:
- var = binding->valueAsString(&qmlUnit->header);
- break;
- case QV4::CompiledData::Binding::Type_Number:
- var = binding->valueAsNumber();
- break;
- case QV4::CompiledData::Binding::Type_Boolean:
- var = binding->valueAsBoolean();
- break;
- case QV4::CompiledData::Binding::Type_Translation:
- case QV4::CompiledData::Binding::Type_TranslationById:
- ds << binding->value.translationData.commentIndex << binding->value.translationData.number;
- var = binding->stringIndex;
- default:
- break;
- }
- ds << var;
- }
+ if (decoded)
+ return;
+
+ foreach (const QV4::CompiledData::Binding *binding, bindings)
+ decodeBinding(QString(), cdata->qmlUnit, binding);
+
+ bindings.clear();
- return rv;
+ decoded = true;
}
-void QQuickPropertyChangesPrivate::decode()
+void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, const QV4::CompiledData::QmlUnit *qmlUnit, const QV4::CompiledData::Binding *binding)
{
Q_Q(QQuickPropertyChanges);
- if (decoded)
- return;
- QDataStream ds(&data, QIODevice::ReadOnly);
+ QString propertyName = propertyPrefix + qmlUnit->header.stringAt(binding->propertyNameIndex);
- int count;
- ds >> count;
- for (int ii = 0; ii < count; ++ii) {
- QString name;
- int type;
- QVariant data;
- QQmlBinding::Identifier id = QQmlBinding::Invalid;
- QV4::CompiledData::TranslationData tsd;
- ds >> name;
- ds >> type;
-
- if (type == QV4::CompiledData::Binding::Type_Script) {
- ds >> id;
- } else if (type == QV4::CompiledData::Binding::Type_Translation
- || type == QV4::CompiledData::Binding::Type_TranslationById) {
- ds >> tsd.commentIndex >> tsd.number;
+ if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty
+ || binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+ QString pre = propertyName + QLatin1Char('.');
+ const QV4::CompiledData::Object *subObj = qmlUnit->objectAt(binding->value.objectIndex);
+ const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
+ for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) {
+ decodeBinding(pre, qmlUnit, subBinding);
}
+ return;
+ }
- ds >> data;
-
- QQmlProperty prop = property(name); //### better way to check for signal property?
- if (prop.type() & QQmlProperty::SignalProperty) {
- QQuickReplaceSignalHandler *handler = new QQuickReplaceSignalHandler;
- handler->property = prop;
- handler->expression.take(new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(prop)->signalIndex(),
- QQmlContextData::get(qmlContext(q)), object, cdata->functionForBindingId(id)));
- signalReplacements << handler;
- } else if (type == QV4::CompiledData::Binding::Type_Script) { // binding
- QString expression = data.toString();
- QUrl url = QUrl();
- int line = -1;
- int column = -1;
-
- QQmlData *ddata = QQmlData::get(q);
- if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) {
- url = ddata->outerContext->url;
- line = ddata->lineNumber;
- column = ddata->columnNumber;
- }
+ QQmlProperty prop = property(propertyName); //### better way to check for signal property?
- expressions << ExpressionChange(name, id, expression, url, line, column);
- } else {
- if (type == QV4::CompiledData::Binding::Type_Translation
- || type == QV4::CompiledData::Binding::Type_TranslationById) {
- QV4::CompiledData::Binding tmpBinding;
- tmpBinding.type = type;
- tmpBinding.stringIndex = data.toInt();
- tmpBinding.value.translationData = tsd;
- data = tmpBinding.valueAsString(&cdata->qmlUnit->header);
- }
- properties << qMakePair(name, data);
+ if (prop.type() & QQmlProperty::SignalProperty) {
+ QQuickReplaceSignalHandler *handler = new QQuickReplaceSignalHandler;
+ handler->property = prop;
+ handler->expression.take(new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(prop)->signalIndex(),
+ QQmlContextData::get(qmlContext(q)), object, cdata->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]));
+ signalReplacements << handler;
+ return;
+ }
+
+ if (binding->type == QV4::CompiledData::Binding::Type_Script) {
+ QString expression = binding->valueAsString(&qmlUnit->header);
+ QUrl url = QUrl();
+ int line = -1;
+ int column = -1;
+
+ QQmlData *ddata = QQmlData::get(q);
+ if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) {
+ url = ddata->outerContext->url;
+ line = ddata->lineNumber;
+ column = ddata->columnNumber;
}
+
+ expressions << ExpressionChange(propertyName, binding->value.compiledScriptIndex, expression, url, line, column);
+ return;
}
- decoded = true;
- data.clear();
+
+ QVariant var;
+ switch (binding->type) {
+ case QV4::CompiledData::Binding::Type_Script:
+ Q_UNREACHABLE();
+ case QV4::CompiledData::Binding::Type_Translation:
+ case QV4::CompiledData::Binding::Type_TranslationById:
+ case QV4::CompiledData::Binding::Type_String:
+ var = binding->valueAsString(&qmlUnit->header);
+ break;
+ case QV4::CompiledData::Binding::Type_Number:
+ var = binding->valueAsNumber();
+ break;
+ case QV4::CompiledData::Binding::Type_Boolean:
+ var = binding->valueAsBoolean();
+ break;
+ default:
+ break;
+ }
+
+ properties << qMakePair(propertyName, var);
}
-void QQuickPropertyChangesParser::setCustomData(QObject *object, const QByteArray &data, QQmlCompiledData *cdata)
+void QQuickPropertyChangesParser::verifyBindings(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props)
+{
+ for (int ii = 0; ii < props.count(); ++ii)
+ verifyList(qmlUnit, props.at(ii));
+}
+
+void QQuickPropertyChangesParser::applyBindings(QObject *obj, QQmlCompiledData *cdata, const QList<const QV4::CompiledData::Binding *> &bindings)
{
QQuickPropertyChangesPrivate *p =
- static_cast<QQuickPropertyChangesPrivate *>(QObjectPrivate::get(object));
- p->data = data;
+ static_cast<QQuickPropertyChangesPrivate *>(QObjectPrivate::get(obj));
+ p->bindings = bindings;
p->cdata = cdata;
p->decoded = false;
}
@@ -479,9 +454,17 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions()
a.specifiedObject = d->object;
a.specifiedProperty = property;
- QQmlBinding *newBinding = e.id != QQmlBinding::Invalid ? QQmlBinding::createBinding(e.id, object(), qmlContext(this)) : 0;
+ QQmlContextData *context = QQmlContextData::get(qmlContext(this));
+
+ QQmlBinding *newBinding = 0;
+ if (e.id != QQmlBinding::Invalid) {
+ QV4::Scope scope(QQmlEnginePrivate::getV4Engine(qmlEngine(this)));
+ QV4::ScopedValue function(scope, QV4::QmlBindingWrapper::createQmlCallableForFunction(context, object(), d->cdata->compilationUnit->runtimeFunctions[e.id]));
+ newBinding = new QQmlBinding(function, object(), context);
+ }
+// QQmlBinding *newBinding = e.id != QQmlBinding::Invalid ? QQmlBinding::createBinding(e.id, object(), qmlContext(this)) : 0;
if (!newBinding)
- newBinding = new QQmlBinding(e.expression, object(), QQmlContextData::get(qmlContext(this)), e.url.toString(), e.line, e.column);
+ newBinding = new QQmlBinding(e.expression, object(), context, e.url.toString(), e.line, e.column);
if (d->isExplicit) {
// in this case, we don't want to assign a binding, per se,