QQmlObjectCreator: Do not crash on read-only bindable
If the binding was not actually set (because the bindable is readonly) then it's dead after the pop_front. We cannot examine it anymore, and we don't have to. Pick-to: 6.5 6.4 6.2 Fixes: QTBUG-109597 Change-Id: I3bf0ca501aa9ad45a64ad181b685ca6d9d325231 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
0461a51c8e
commit
4dfcaa7ee8
|
@ -1442,16 +1442,24 @@ bool QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt)
|
||||||
void *argv[] = { &bindable };
|
void *argv[] = { &bindable };
|
||||||
// allow interception
|
// allow interception
|
||||||
target->metaObject()->metacall(target, QMetaObject::BindableProperty, index, argv);
|
target->metaObject()->metacall(target, QMetaObject::BindableProperty, index, argv);
|
||||||
bindable.setBinding(qmlBinding);
|
const bool success = bindable.setBinding(qmlBinding);
|
||||||
|
|
||||||
|
// Only pop_front after setting the binding as the bindings are refcounted.
|
||||||
sharedState->allQPropertyBindings.pop_front();
|
sharedState->allQPropertyBindings.pop_front();
|
||||||
if (auto priv = QPropertyBindingPrivate::get(qmlBinding); priv->hasCustomVTable()) {
|
|
||||||
auto qmlBindingPriv = static_cast<QQmlPropertyBinding *>(priv);
|
// If the binding was actually not set, it's deleted now.
|
||||||
auto jsExpression = qmlBindingPriv->jsExpression();
|
if (success) {
|
||||||
const bool canRemove = !qmlBinding.error().hasError() && !qmlBindingPriv->hasDependencies()
|
if (auto priv = QPropertyBindingPrivate::get(qmlBinding); priv->hasCustomVTable()) {
|
||||||
&& !jsExpression->hasUnresolvedNames();
|
auto qmlBindingPriv = static_cast<QQmlPropertyBinding *>(priv);
|
||||||
if (canRemove)
|
auto jsExpression = qmlBindingPriv->jsExpression();
|
||||||
bindable.takeBinding();
|
const bool canRemove = !qmlBinding.error().hasError()
|
||||||
|
&& !qmlBindingPriv->hasDependencies()
|
||||||
|
&& !jsExpression->hasUnresolvedNames();
|
||||||
|
if (canRemove)
|
||||||
|
bindable.takeBinding();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (watcher.hasRecursed() || interrupt.shouldInterrupt())
|
if (watcher.hasRecursed() || interrupt.shouldInterrupt())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import Qt.test
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
ReadOnlyBindable {
|
||||||
|
property int v: 12
|
||||||
|
x: v
|
||||||
|
}
|
|
@ -541,6 +541,7 @@ void registerTypes()
|
||||||
|
|
||||||
qmlRegisterType<Receiver>("Qt.test", 1,0, "Receiver");
|
qmlRegisterType<Receiver>("Qt.test", 1,0, "Receiver");
|
||||||
qmlRegisterType<Sender>("Qt.test", 1,0, "Sender");
|
qmlRegisterType<Sender>("Qt.test", 1,0, "Sender");
|
||||||
|
qmlRegisterTypesAndRevisions<ReadOnlyBindable>("Qt.test", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "testtypes.moc"
|
#include "testtypes.moc"
|
||||||
|
|
|
@ -1981,6 +1981,25 @@ public slots:
|
||||||
int slot1(int i, int j, int k) {return i+j+k;}
|
int slot1(int i, int j, int k) {return i+j+k;}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ReadOnlyBindable : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
Q_PROPERTY(int x READ x WRITE setX BINDABLE bindableX)
|
||||||
|
Q_OBJECT_BINDABLE_PROPERTY(ReadOnlyBindable, int, _xProp)
|
||||||
|
|
||||||
|
public:
|
||||||
|
ReadOnlyBindable(QObject *parent = nullptr)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
setX(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
int x() const { return _xProp.value(); }
|
||||||
|
void setX(int x) { _xProp.setValue(x); }
|
||||||
|
QBindable<int> bindableX() const { return &_xProp; }
|
||||||
|
};
|
||||||
|
|
||||||
void registerTypes();
|
void registerTypes();
|
||||||
|
|
||||||
#endif // TESTTYPES_H
|
#endif // TESTTYPES_H
|
||||||
|
|
|
@ -414,6 +414,8 @@ private slots:
|
||||||
void internalClassParentGc();
|
void internalClassParentGc();
|
||||||
void methodTypeMismatch();
|
void methodTypeMismatch();
|
||||||
|
|
||||||
|
void doNotCrashOnReadOnlyBindable();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
|
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
|
||||||
static void verifyContextLifetime(const QQmlRefPointer<QQmlContextData> &ctxt);
|
static void verifyContextLifetime(const QQmlRefPointer<QQmlContextData> &ctxt);
|
||||||
|
@ -10355,6 +10357,22 @@ void tst_qqmlecmascript::methodTypeMismatch()
|
||||||
QCOMPARE(object->actuals(), QVariantList() << QVariant::fromValue((QObject *)nullptr));
|
QCOMPARE(object->actuals(), QVariantList() << QVariant::fromValue((QObject *)nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_qqmlecmascript::doNotCrashOnReadOnlyBindable()
|
||||||
|
{
|
||||||
|
QQmlEngine engine;
|
||||||
|
QQmlComponent c(&engine, testFileUrl("readOnlyBindable.qml"));
|
||||||
|
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
QTest::ignoreMessage(
|
||||||
|
QtWarningMsg,
|
||||||
|
"setBinding: Could not set binding via bindable interface. "
|
||||||
|
"The QBindable is read-only.");
|
||||||
|
#endif
|
||||||
|
QScopedPointer<QObject> o(c.create());
|
||||||
|
QVERIFY(o);
|
||||||
|
QCOMPARE(o->property("x").toInt(), 7);
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_qqmlecmascript)
|
QTEST_MAIN(tst_qqmlecmascript)
|
||||||
|
|
||||||
#include "tst_qqmlecmascript.moc"
|
#include "tst_qqmlecmascript.moc"
|
||||||
|
|
Loading…
Reference in New Issue