Prevent the evaluation of bindings if the target has been deleted.
Also, mark an item as queued for deletion at the beginning of the
destructor call chain, so that bindings triggered by the operation
of the destructor itself are not evaluated (after the context is
destructed, if necessary).
Task-number: QTBUG-25516
Change-Id: I587ef7923eb749eb7980156ad73822c1fb7c1ff3
Reviewed-by: Michael Brasser <michael.brasser@nokia.com>
if (!enabledFlag() || !context() || !context()->isValid())
return;
+ // Check that the target has not been deleted
+ if (QQmlData::wasDeleted(object()))
+ return;
+
QQmlTrace trace("General Binding Update");
trace.addDetail("URL", m_url);
trace.addDetail("Line", m_lineNumber);
QHash<int, QObject *> *attachedProperties() const;
static inline bool wasDeleted(QObject *);
+
+ static void markAsDeleted(QObject *);
+ static inline void setQueuedForDeletion(QObject *);
+
private:
// For objectNameNotifier and attachedProperties
mutable QQmlDataExtended *extendedData;
if (!object)
return true;
- QObjectPrivate *priv = QObjectPrivate::get(const_cast<QObject *>(object));
+ QObjectPrivate *priv = QObjectPrivate::get(object);
if (priv->wasDeleted)
return true;
static_cast<QQmlData *>(priv->declarativeData)->isQueuedForDeletion;
}
+void QQmlData::setQueuedForDeletion(QObject *object)
+{
+ if (object) {
+ if (QObjectPrivate *priv = QObjectPrivate::get(object)) {
+ if (!priv->wasDeleted && priv->declarativeData) {
+ static_cast<QQmlData *>(priv->declarativeData)->isQueuedForDeletion = true;
+ }
+ }
+ }
+}
+
QQmlNotifierEndpoint *QQmlData::notify(int index)
{
Q_ASSERT(index <= 0xFFFF);
d->context->destroy();
d->context = 0;
}
+
+ // Mark this object as in the process of deletion to
+ // prevent it resolving in bindings
+ QQmlData::markAsDeleted(o);
}
}
return count;
}
+void QQmlData::markAsDeleted(QObject *o)
+{
+ QQmlData::setQueuedForDeletion(o);
+
+ QObjectPrivate *p = QObjectPrivate::get(o);
+ for (QList<QObject *>::iterator it = p->children.begin(), end = p->children.end(); it != end; ++it) {
+ QQmlData::markAsDeleted(*it);
+ }
+}
+
void QQmlEnginePrivate::init()
{
Q_Q(QQmlEngine);
if (!context || !context->isValid())
return;
+ // Check that the target has not been deleted
+ if (QQmlData::wasDeleted(binding->target))
+ return;
+
QQmlTrace trace("V4 Binding Update");
trace.addDetail("URL", context->url);
trace.addDetail("Line", binding->line);
if (!enabledFlag())
return;
+ QQmlContextData *context = parent->context();
+ if (!context || !context->isValid())
+ return;
+
+ // Check that the target has not been deleted
+ if (QQmlData::wasDeleted(object()))
+ return;
+
QQmlTrace trace("V8 Binding Update");
trace.addDetail("URL", parent->url());
trace.addDetail("Line", instruction->line);
QQmlBindingProfiler prof(parent->urlString(), instruction->line, instruction->column);
- QQmlContextData *context = parent->context();
- if (!context || !context->isValid())
- return;
-
if (!updatingFlag()) {
setUpdatingFlag(true);
+
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine);
bool isUndefined = false;
--- /dev/null
+import QtQuick 2.0
+
+Item {
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ property bool validParentChildCount: parent && (parent.children.length > 0)
+
+ onValidParentChildCountChanged: {
+ if (!validParentChildCount) console.warn('WARNING: Invalid parent child count')
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ Component.onCompleted: {
+ var container = containerComponent.createObject(root)
+ contentComponent.createObject(container)
+ container.destroy();
+
+ pendingEvents.process()
+ }
+
+ property Component containerComponent: ContainerComponent {}
+ property Component contentComponent: ContentComponent {}
+}
+
void replaceBinding();
void deleteRootObjectInCreation();
void onDestruction();
+ void bindingSuppression();
private:
static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
}
}
+struct EventProcessor : public QObject
+{
+ Q_OBJECT
+public:
+ Q_INVOKABLE void process()
+ {
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+ }
+};
+
+void tst_qqmlecmascript::bindingSuppression()
+{
+ QQmlEngine engine;
+ EventProcessor processor;
+ engine.rootContext()->setContextProperty("pendingEvents", &processor);
+
+ transientErrorsMsgCount = 0;
+ QtMsgHandler old = qInstallMsgHandler(transientErrorsMsgHandler);
+
+ QQmlComponent c(&engine, testFileUrl("bindingSuppression.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj != 0);
+ delete obj;
+
+ qInstallMsgHandler(old);
+ QCOMPARE(transientErrorsMsgCount, 0);
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"