Appending object with Javascript array to a QML Li

2019-05-10 14:21发布

问题:

I am using QtQuick on Android.

I am trying to append a Javascript object with an array property to a ListModel.

I use LocalStorage to store this data.

The object I materialise from the database has this array property, and when I try and append that object to the ListModel I get a SEGFAULT. If I do not include an array in my object, or in fact if the array is empty, then it will successfully append to the ListModel and will not SEGFAULT.

When I materialise the object from the database, I do this:

var movie = {
    id: row.id,
    title: row.title,
    genres: ['a', 'b', 'c']
}

I'm creating a Javascript object and copying properties from the LocalStorage result-set object 'row'. For the sake of this issue, I just hard-code the array as you can see above. When this object is appended to the ListModel it SEGFAULTs.

If I remove the genres attribute completely, or even leave it empty as in [], it will not SEGFAULT when appended:

var movie = {
    id: row.id,
    title: row.title,
    genres: []
}

Here is the stack trace:

0   ListModel::set(int, QV4::Referenced<QV4::Object>, QV8Engine*)   /home/linux/qt/Qt5.2.1/5.2.1/android_armv7/lib/libQt5Qml.so     0x7542b138  
1   ListModel::append(QV4::Referenced<QV4::Object>, QV8Engine*) /home/linux/qt/Qt5.2.1/5.2.1/android_armv7/lib/libQt5Qml.so     0x7542b40a  
2   ListModel::set(int, QV4::Referenced<QV4::Object>, QV8Engine*)   /home/linux/qt/Qt5.2.1/5.2.1/android_armv7/lib/libQt5Qml.so     0x7542b26c  
3   ListModel::append(QV4::Referenced<QV4::Object>, QV8Engine*) /home/linux/qt/Qt5.2.1/5.2.1/android_armv7/lib/libQt5Qml.so     0x7542b40a  
4   QQmlListModel::append(QQmlV4Function*)  /home/linux/qt/Qt5.2.1/5.2.1/android_armv7/lib/libQt5Qml.so     0x7542d21c  
5   QQmlListModel::qt_metacall(QMetaObject::Call, int, void**)  /home/linux/qt/Qt5.2.1/5.2.1/android_armv7/lib/libQt5Qml.so     0x754347ea  
6   QMetaObject::metacall(QObject*, QMetaObject::Call, int, void**) /home/linux/qt/Qt5.2.1/5.2.1/android_armv7/lib/libQt5Core.so        0x75054416  
7   QV4::QObjectMethod::callInternal(QV4::CallData*)    /home/linux/qt/Qt5.2.1/5.2.1/android_armv7/lib/libQt5Qml.so     0x753ac7c4  
8   QV4::FunctionObject::call(QV4::CallData*)   /home/linux/qt/Qt5.2.1/5.2.1/android_armv7/lib/libQt5Qml.so     0x7537ed5e  
9   QV4::__qmljs_call_property(QV4::ExecutionContext*, QV4::Referenced<QV4::String>, QV4::CallDataRef)  /home/linux/qt/Qt5.2.1/5.2.1/android_armv7/lib/libQt5Qml.so     0x75380962  
10  ??          0x7727c2e8  
11  ??          0x7727c2e8  

After building Qt with full debug symbols and single-stepping, the crash is here:

void ListModel::set(int elementIndex, QV4::ObjectRef object, QV8Engine *eng)
{
    ListElement *e = elements[elementIndex];

    QV4::ExecutionEngine *v4 = object->engine(); <= SEGFAULT

When stepping through the code it seems when it encounters the Javascript array value for the genres property, the ListModel is attempting to create a sub-model and then it fails. This is called as a result of this piece of code also in the ListModel::set function:

} else if (propertyValue->asArrayObject()) {
    a = propertyValue;
    const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
    if (r.type == ListLayout::Role::List) {
        ListModel *subModel = new ListModel(r.subLayout, 0, -1);

        int arrayLength = a->arrayLength();
        for (int j=0 ; j < arrayLength ; ++j) {
            o = a->getIndexed(j);
            subModel->append(o, eng); <= This leads to the nested 'set' call above that crashes
        }

So why is this failing?

回答1:

This fails because it is not supported to store non-object values such as literal strings or integers as property values in an array.

An array property can be stored, but only if each item in the array is an object, not a primitive or string literal.

This works:

var movie = {
    id: row.id,
    title: row.title,
    genres: [{name: 'a'}, {name: 'b'}, {name: 'c'}]
}