[new compiler] Fix property initialization order for mixed list and default property...
authorSimon Hausmann <simon.hausmann@digia.com>
Tue, 4 Mar 2014 14:26:18 +0000 (15:26 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Tue, 4 Mar 2014 15:55:43 +0000 (16:55 +0100)
Get a little closer to replicating the old compiler's behavior without
introducing a new nested data structure for lists:

* List property assignments should happen in declaration order for the items.
  Instead of doing magic in QmlObject::appendBinding, simply traverse the UiArrayBinding
  members in reverse order. Within a list, the items remain in order then, due to
  QmlObject::appendItem prepending. In the overall picture for the entire object,
  the reverse initialization order for properties is also preserved this way.

* When an object has property bindings to the default property and also bindings
  to a named property that - after meta-object determination - turns out to be
  the default property, then we need to merge the bindings and preserve the
  declaration order. (tst_qqmlecmascript::defaultPropertyListOrder checks that)

Fixes tst_qqmlenginedebugservice that expects bindings to an entire list to
happen in reverse order (like other properties).

Change-Id: I7408c97cdb971e06b1ee43a2a85f8cc6f008c444
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/qml/compiler/qqmlcodegenerator.cpp
src/qml/compiler/qqmlcodegenerator_p.h
src/qml/compiler/qqmltypecompiler.cpp
src/qml/compiler/qqmltypecompiler_p.h

index 336c3a1..971d85e 100644 (file)
@@ -176,14 +176,10 @@ QString QmlObject::appendBinding(Binding *b, bool isListBinding)
         if (existing && existing->isValueBinding() == b->isValueBinding() && !(existing->flags & QV4::CompiledData::Binding::IsOnAssignment))
             return tr("Property value set multiple times");
     }
-    if (isListBinding) {
-        bindings->append(b);
-    } else if (bindingToDefaultProperty) {
-        Binding *insertionPoint = bindings->findSortedInsertionPoint<QV4::CompiledData::Location, QV4::CompiledData::Binding, &QV4::CompiledData::Binding::location>(b);
-        bindings->insertAfter(insertionPoint, b);
-    } else {
+    if (bindingToDefaultProperty)
+        insertSorted(b);
+    else
         bindings->prepend(b);
-    }
     return QString(); // no error
 }
 
@@ -195,6 +191,12 @@ Binding *QmlObject::findBinding(quint32 nameIndex) const
     return 0;
 }
 
+void QmlObject::insertSorted(Binding *b)
+{
+    Binding *insertionPoint = bindings->findSortedInsertionPoint<QV4::CompiledData::Location, QV4::CompiledData::Binding, &QV4::CompiledData::Binding::valueLocation>(b);
+    bindings->insertAfter(insertionPoint, b);
+}
+
 QStringList Signal::parameterStringList(const QStringList &stringPool) const
 {
     QStringList result;
@@ -361,16 +363,20 @@ bool QQmlCodeGenerator::visit(QQmlJS::AST::UiArrayBinding *node)
         return false;
     }
 
+    QVarLengthArray<QQmlJS::AST::UiArrayMemberList *, 16> memberList;
     QQmlJS::AST::UiArrayMemberList *member = node->members;
     while (member) {
+        memberList.append(member);
+        member = member->next;
+    }
+    for (int i = memberList.count() - 1; i >= 0; --i) {
+        member = memberList.at(i);
         QQmlJS::AST::UiObjectDefinition *def = QQmlJS::AST::cast<QQmlJS::AST::UiObjectDefinition*>(member->member);
 
         int idx = 0;
         if (!defineQMLObject(&idx, def))
             return false;
         appendBinding(qualifiedNameLocation, name->identifierToken, propertyNameIndex, idx, /*isListItem*/ true);
-
-        member = member->next;
     }
 
     qSwap(_object, object);
index fdb6891..3b72e24 100644 (file)
@@ -137,6 +137,25 @@ struct PoolList
         }
     }
 
+    T *unlink(T *before, T *item) {
+        T * const newNext = item->next;
+
+        if (before)
+            before->next = newNext;
+        else
+            first = newNext;
+
+        if (item == last) {
+            if (newNext)
+                last = newNext;
+            else
+                last = first;
+        }
+
+        --count;
+        return newNext;
+    }
+
     T *slowAt(int index) const
     {
         T *result = first;
@@ -275,6 +294,8 @@ public:
 
     QString appendBinding(Binding *b, bool isListBinding);
     Binding *findBinding(quint32 nameIndex) const;
+    Binding *unlinkBinding(Binding *before, Binding *binding) { return bindings->unlink(before, binding); }
+    void insertSorted(Binding *b);
 
     PoolList<CompiledFunctionOrExpression> *functionsAndExpressions;
     FixedPoolArray<int> *runtimeFunctionIndices;
index 780b3da..dfea506 100644 (file)
@@ -143,6 +143,11 @@ bool QQmlTypeCompiler::compile()
     }
 
     {
+        QQmlDefaultPropertyMerger merger(this);
+        merger.mergeDefaultProperties();
+    }
+
+    {
         SignalHandlerConverter converter(this);
         if (!converter.convertSignalHandlerExpressionsToFunctionDeclarations())
             return false;
@@ -2358,4 +2363,60 @@ bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIn
     return true;
 }
 
+QQmlDefaultPropertyMerger::QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler)
+    : QQmlCompilePass(typeCompiler)
+    , qmlObjects(*typeCompiler->qmlObjects())
+    , propertyCaches(typeCompiler->propertyCaches())
+{
+
+}
+
+void QQmlDefaultPropertyMerger::mergeDefaultProperties()
+{
+    for (int i = 0; i < qmlObjects.count(); ++i)
+        mergeDefaultProperties(i);
+}
+
+void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex)
+{
+    QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex);
+    if (!propertyCache)
+        return;
+
+    QmlObject *object = qmlObjects.at(objectIndex);
+
+    QString defaultProperty = object->indexOfDefaultProperty != -1 ? propertyCache->parent()->defaultPropertyName() : propertyCache->defaultPropertyName();
+    Binding *bindingsToReinsert = 0;
+    Binding *tail = 0;
+
+    Binding *previousBinding = 0;
+    Binding *binding = object->firstBinding();
+    while (binding) {
+        if (binding->propertyNameIndex == 0 || stringAt(binding->propertyNameIndex) != defaultProperty) {
+            previousBinding = binding;
+            binding = binding->next;
+            continue;
+        }
+
+        Binding *toReinsert = binding;
+        binding = object->unlinkBinding(previousBinding, binding);
+
+        if (!tail) {
+            bindingsToReinsert = toReinsert;
+            tail = toReinsert;
+        } else {
+            tail->next = toReinsert;
+            tail = tail->next;
+        }
+        tail->next = 0;
+    }
+
+    binding = bindingsToReinsert;
+    while (binding) {
+        Binding *toReinsert = binding;
+        binding = binding->next;
+        object->insertSorted(toReinsert);
+    }
+}
+
 QT_END_NAMESPACE
index 0f32f63..62c0ae8 100644 (file)
@@ -287,6 +287,20 @@ private:
     QtQml::JSCodeGen * const v4CodeGen;
 };
 
+class QQmlDefaultPropertyMerger : public QQmlCompilePass
+{
+public:
+    QQmlDefaultPropertyMerger(QQmlTypeCompiler *typeCompiler);
+
+    void mergeDefaultProperties();
+
+private:
+    void mergeDefaultProperties(int objectIndex);
+
+    const QList<QtQml::QmlObject*> &qmlObjects;
+    const QVector<QQmlPropertyCache*> &propertyCaches;
+};
+
 QT_END_NAMESPACE
 
 #endif // QQMLTYPECOMPILER_P_H