QQmlListProperty ошибочные сбои с передачей прав собственности на JavaScript

Я испытываю некоторые серьезные носовые демоны с Qt5 (5.8, Arch Linux) и QML. По сути, код предоставляет QQmlListProperty с его членами, созданными на лету, и передающими свои права на JavaScript. Когда сигнал itemChanged() испускается тысячи раз (в моем реальном коде достаточно только пары раз), программа вызывает ошибку.

Согласно Valgrind + GDB, похоже, что 'A' освобождается QObject::event, а затем читается QML, вызывая segfault.

Как ни странно, даже если изменить имя переменной 'game' на что-то другое, то количество раз, когда мне приходится нажимать кнопку для сбоя программы, меняется. Удаление повторителя или прямоугольника или использование другого выражения для цвета прямоугольника влияет на зависание программы или нет. Кроме того, всякий раз, когда право собственности не передается JS, не возникает никаких проблем. К сожалению, это также вызывает утечки памяти, поскольку ничто не освобождает объекты "А".

Из-за этих деталей я считаю, что где-то в коде есть неопределенное поведение, но я понятия не имею, где.

Это самый короткий пример, который я мог бы создать:

main.cpp:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QObject>
#include <QQmlListProperty>

template <typename T>
T* make_js() {
    T* t = new T();
    QQmlEngine::setObjectOwnership(t, QQmlEngine::JavaScriptOwnership);
    return t;
}

class A: public QObject {
    Q_OBJECT
    Q_PROPERTY(int id READ id CONSTANT)
public:
    int id() const {return 1;}
};

class B: public QObject {
    Q_OBJECT
    Q_PROPERTY(A* item READ item NOTIFY itemChanged)
    Q_PROPERTY(QQmlListProperty<A> list READ list CONSTANT)
public:

    A *item() const { return make_js<A>(); }

    Q_INVOKABLE void change() { emit itemChanged(); }

    QQmlListProperty<A> list() {
        return QQmlListProperty<A>(
            this, nullptr,
            [](QQmlListProperty<A> *) {return 1;},
            [](QQmlListProperty<A> *, int ) {return make_js<A>();});
    }
signals:
    void itemChanged();
};

#include "main.moc"

int main(int argc, char *argv[]) {
    QGuiApplication a(argc, argv);

    B* game = new B();

    QQmlApplicationEngine engine;

    qmlRegisterType<B>("example", 1, 0, "B");
    qmlRegisterType<A>("example", 1, 0, "A");
    engine.rootContext()->setContextProperty("thing", game);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    a.exec();

    delete game;
    return 0;
}

main.qml:

import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Window 2.0
import QtQuick.Controls 2.0
import example 1.0

Window {
    visible: true
    width: 1280
    height: 800
    title: qsTr("Example")

    ColumnLayout {
        anchors.fill: parent

        Repeater {
            model: thing.list

            Rectangle {
                color: thing.item.id === 1 ? "red" : "blue"
            }
        }

        Item {
            Layout.fillWidth: true
            Layout.fillHeight: true

            Button {
                text: "Click me a bunch of times to crash!"
                anchors.fill: parent
                onClicked: {for(var i=0;i<1000;++i) thing.change();}
            }
        }
    }
}

В чем причина такого поведения? Вы знаете об обходном пути для этого? Может ли быть так, что QQmlListProperty каким-то образом не совместим с владением JavaScript?

РЕДАКТИРОВАТЬ: я нашел обходной путь, который достаточно хорош для моего использования. изменения

Q_PROPERTY(QQmlListProperty<A> list READ list CONSTANT)

в

Q_PROPERTY(QQmlListProperty<A> list READ list NOTIFY listChanged)

и испуская listChanged() в change() вместе с itemChanged() исправляет это. При дополнительном тестировании я заметил, что Qt только один раз вызывает индексную лямбду из list(), а затем освобождает возвращаемый объект "A". Он больше не вызывает лямбду и, кажется, пытается повторно использовать уже освобожденную букву "А".

Тем не менее, я все еще хотел бы знать, что вызывает это в первую очередь.

0 ответов

Другие вопросы по тегам