Fix translations in states causing failing assertions
authorSimon Hausmann <simon.hausmann@digia.com>
Sat, 26 Apr 2014 12:42:39 +0000 (14:42 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Mon, 28 Apr 2014 10:50:17 +0000 (12:50 +0200)
This is a smaller fix suitable for the release branch, merely adding support
for translations to the bytearray compilation step for states and ensuring a
consistent error message when qsTr is used in list models.

The proper fix will be done in dev that eliminates the entire intermediate
QByteArray storage for custom compilers.

Task-number: QTBUG-38492
Change-Id: If5171f16eb742c718e48b8bbcb265b0c241cd5e7
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/qml/types/qqmllistmodel.cpp
src/quick/util/qquickpropertychanges.cpp
tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp
tests/auto/quick/qquickstates/data/QTBUG-38492.qml [new file with mode: 0644]
tests/auto/quick/qquickstates/tst_qquickstates.cpp

index 8eab51a..0056276 100644 (file)
@@ -2325,6 +2325,10 @@ bool QQmlListModelParser::compileProperty(const QV4::CompiledData::QmlUnit *qmlU
         } 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;
         } else if (binding->type == QV4::CompiledData::Binding::Type_Script) {
             QString scriptStr = binding->valueAsScriptString(&qmlUnit->header);
             if (definesEmptyList(scriptStr)) {
index 188b91b..1786317 100644 (file)
@@ -272,12 +272,11 @@ QByteArray QQuickPropertyChangesParser::compile(const QV4::CompiledData::QmlUnit
     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;
-        bool isScript = binding->type == QV4::CompiledData::Binding::Type_Script;
-        QQmlBinding::Identifier id = QQmlBinding::Invalid;
         switch (binding->type) {
         case QV4::CompiledData::Binding::Type_Script:
-            id = bindingIdentifier(binding);
+            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:
@@ -291,13 +290,12 @@ QByteArray QQuickPropertyChangesParser::compile(const QV4::CompiledData::QmlUnit
             break;
         case QV4::CompiledData::Binding::Type_Translation:
         case QV4::CompiledData::Binding::Type_TranslationById:
-            Q_UNREACHABLE();
+            ds << binding->value.translationData.commentIndex << binding->value.translationData.number;
+            var = binding->stringIndex;
         default:
             break;
         }
-        ds << data.at(ii).first << isScript << var;
-        if (isScript)
-            ds << id;
+        ds << var;
     }
 
     return rv;
@@ -315,14 +313,21 @@ void QQuickPropertyChangesPrivate::decode()
     ds >> count;
     for (int ii = 0; ii < count; ++ii) {
         QString name;
-        bool isScript;
+        int type;
         QVariant data;
         QQmlBinding::Identifier id = QQmlBinding::Invalid;
+        QV4::CompiledData::TranslationData tsd;
         ds >> name;
-        ds >> isScript;
-        ds >> data;
-        if (isScript)
+        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;
+        }
+
+        ds >> data;
 
         QQmlProperty prop = property(name);      //### better way to check for signal property?
         if (prop.type() & QQmlProperty::SignalProperty) {
@@ -331,7 +336,7 @@ void QQuickPropertyChangesPrivate::decode()
             handler->expression.take(new QQmlBoundSignalExpression(object, QQmlPropertyPrivate::get(prop)->signalIndex(),
                                                                    QQmlContextData::get(qmlContext(q)), object, cdata->functionForBindingId(id)));
             signalReplacements << handler;
-        } else if (isScript) { // binding
+        } else if (type == QV4::CompiledData::Binding::Type_Script) { // binding
             QString expression = data.toString();
             QUrl url = QUrl();
             int line = -1;
@@ -346,6 +351,14 @@ void QQuickPropertyChangesPrivate::decode()
 
             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);
         }
     }
index 678849c..9b57eef 100644 (file)
@@ -101,6 +101,8 @@ private slots:
     void static_types_data();
     void static_i18n();
     void static_i18n_data();
+    void dynamic_i18n();
+    void dynamic_i18n_data();
     void static_nestedElements();
     void static_nestedElements_data();
     void dynamic_data();
@@ -341,6 +343,54 @@ void tst_qqmllistmodel::static_i18n()
     delete obj;
 }
 
+void tst_qqmllistmodel::dynamic_i18n_data()
+{
+    QTest::addColumn<QString>("qml");
+    QTest::addColumn<QVariant>("value");
+    QTest::addColumn<QString>("error");
+
+    QTest::newRow("qsTr")
+        << QString::fromUtf8("ListElement { foo: qsTr(\"test\") }")
+        << QVariant(QString::fromUtf8("test"))
+        << QString("ListElement: cannot use script for property value");
+
+    QTest::newRow("qsTrId")
+        << "ListElement { foo: qsTrId(\"qtn_test\") }"
+        << QVariant(QString("qtn_test"))
+        << QString("ListElement: cannot use script for property value");
+}
+
+void tst_qqmllistmodel::dynamic_i18n()
+{
+    QFETCH(QString, qml);
+    QFETCH(QVariant, value);
+    QFETCH(QString, error);
+
+    qml = "import QtQuick 2.0\nItem { property variant test: model.get(0).foo; ListModel { id: model; " + qml + " } }";
+
+    QQmlEngine engine;
+    QQmlComponent component(&engine);
+    component.setData(qml.toUtf8(),
+                      QUrl::fromLocalFile(QString("dummy.qml")));
+
+    if (!error.isEmpty()) {
+        QVERIFY(component.isError());
+        QCOMPARE(component.errors().at(0).description(), error);
+        return;
+    }
+
+    QVERIFY(!component.isError());
+
+    QObject *obj = component.create();
+    QVERIFY(obj != 0);
+
+    QVariant actual = obj->property("test");
+
+    QCOMPARE(actual, value);
+    QCOMPARE(actual.toString(), value.toString());
+
+    delete obj;
+}
 void tst_qqmllistmodel::static_nestedElements()
 {
     QFETCH(int, elementCount);
diff --git a/tests/auto/quick/qquickstates/data/QTBUG-38492.qml b/tests/auto/quick/qquickstates/data/QTBUG-38492.qml
new file mode 100644 (file)
index 0000000..d6d6d81
--- /dev/null
@@ -0,0 +1,16 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    property string text;
+
+    states: [
+        State {
+            name: 'apply'
+            PropertyChanges {
+                target: root
+                text: qsTr("Test")
+            }
+        }
+    ]
+}
index 0c9b756..6b46ab1 100644 (file)
@@ -148,6 +148,7 @@ private slots:
     void QTBUG_14830();
     void avoidFastForward();
     void revertListBug();
+    void QTBUG_38492();
 };
 
 void tst_qquickstates::initTestCase()
@@ -1626,6 +1627,21 @@ void tst_qquickstates::revertListBug()
     QCOMPARE(rect2->parentItem(), origParent2); //QTBUG-22583 causes rect2's parent item to be origParent1
 }
 
+void tst_qquickstates::QTBUG_38492()
+{
+    QQmlEngine engine;
+
+    QQmlComponent rectComponent(&engine, testFileUrl("QTBUG-38492.qml"));
+    QQuickItem *item = qobject_cast<QQuickItem*>(rectComponent.create());
+    QVERIFY(item != 0);
+
+    QQuickItemPrivate::get(item)->setState("apply");
+
+    QCOMPARE(item->property("text").toString(), QString("Test"));
+
+    delete item;
+}
+
 QTEST_MAIN(tst_qquickstates)
 
 #include "tst_qquickstates.moc"