QT_BEGIN_NAMESPACE
-QQmlVMEVariantQObjectPtr::QQmlVMEVariantQObjectPtr()
- : QQmlGuard<QObject>(0), m_target(0), m_index(-1)
+QQmlVMEVariantQObjectPtr::QQmlVMEVariantQObjectPtr(bool isVar)
+ : QQmlGuard<QObject>(0), m_target(0), m_isVar(isVar), m_index(-1)
{
}
{
}
-void QQmlVMEVariantQObjectPtr::objectDestroyed(QObject *)
+void QQmlVMEVariantQObjectPtr::objectDestroyed(QObject *o)
{
- if (m_target && m_index >= 0)
+ if (m_target && m_index >= 0) {
+ if (m_isVar && m_target->varPropertiesInitialized && !m_target->varProperties.IsEmpty()) {
+ // Set the var property to NULL
+ m_target->varProperties->Set(m_index - m_target->firstVarPropertyIndex, v8::Null());
+ }
+
m_target->activate(m_target->object, m_target->methodOffset() + m_index, 0);
+ }
}
void QQmlVMEVariantQObjectPtr::setGuardedValue(QObject *obj, QQmlVMEMetaObject *target, int index)
if (type != QMetaType::QObjectStar) {
cleanup();
type = QMetaType::QObjectStar;
- new (dataPtr()) QQmlVMEVariantQObjectPtr;
+ new (dataPtr()) QQmlVMEVariantQObjectPtr(false);
}
reinterpret_cast<QQmlVMEVariantQObjectPtr*>(dataPtr())->setGuardedValue(v, target, index);
}
if (metaData->varPropertyCount)
qPersistentDispose(varProperties); // if not weak, will not have been cleaned up by the callback.
+
+ qDeleteAll(varObjectGuards);
}
int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a)
}
}
- // And, if the new value is a scarce resource, we need to ensure that it does not get
- // automatically released by the engine until no other references to it exist.
+ QObject *valueObject = 0;
+ QQmlVMEVariantQObjectPtr *guard = getQObjectGuardForProperty(id);
+
if (value->IsObject()) {
- QV8VariantResource *r = v8_resource_cast<QV8VariantResource>(v8::Handle<v8::Object>::Cast(value));
- if (r) {
+ // And, if the new value is a scarce resource, we need to ensure that it does not get
+ // automatically released by the engine until no other references to it exist.
+ if (QV8VariantResource *r = v8_resource_cast<QV8VariantResource>(v8::Handle<v8::Object>::Cast(value))) {
r->addVmePropertyReference();
+ } else if (QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(v8::Handle<v8::Object>::Cast(value))) {
+ // We need to track this QObject to signal its deletion
+ valueObject = r->object;
+
+ // Do we already have a QObject guard for this property?
+ if (valueObject && !guard) {
+ guard = new QQmlVMEVariantQObjectPtr(true);
+ varObjectGuards.append(guard);
+ }
}
}
+ if (guard) {
+ guard->setGuardedValue(valueObject, this, id);
+ }
+
// Write the value and emit change signal as appropriate.
varProperties->Set(id - firstVarPropertyIndex, value);
activate(object, methodOffset() + id, 0);
return vme;
}
+QQmlVMEVariantQObjectPtr *QQmlVMEMetaObject::getQObjectGuardForProperty(int index) const
+{
+ QList<QQmlVMEVariantQObjectPtr *>::ConstIterator it = varObjectGuards.constBegin(), end = varObjectGuards.constEnd();
+ for ( ; it != end; ++it) {
+ if ((*it)->m_index == index) {
+ return *it;
+ }
+ }
+
+ return 0;
+}
+
QT_END_NAMESPACE
class QQmlVMEVariantQObjectPtr : public QQmlGuard<QObject>
{
public:
- inline QQmlVMEVariantQObjectPtr();
+ inline QQmlVMEVariantQObjectPtr(bool isVar);
inline ~QQmlVMEVariantQObjectPtr();
+
inline void objectDestroyed(QObject *);
inline void setGuardedValue(QObject *obj, QQmlVMEMetaObject *target, int index);
QQmlVMEMetaObject *m_target;
- int m_index;
+ unsigned m_isVar : 1;
+ int m_index : 31;
};
class QV8QObjectWrapper;
void activate(QObject *, int, void **);
+ QList<QQmlVMEVariantQObjectPtr *> varObjectGuards;
+
+ QQmlVMEVariantQObjectPtr *getQObjectGuardForProperty(int) const;
+
friend class QV8GCCallback;
friend class QV8QObjectWrapper;
};
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ property bool success: false
+
+ Component {
+ id: internal
+
+ Item {
+ }
+ }
+
+ property bool expectNull: null
+
+ function setExpectNull(b) {
+ success = false;
+ expectNull = b;
+ }
+
+ property QtObject obj: null
+ onObjChanged: success = (expectNull ? obj == null : obj != null)
+
+ Component.onCompleted: {
+ setExpectNull(false)
+ obj = internal.createObject(null, {})
+ if (!success) return
+
+ // Replace with a different object
+ setExpectNull(false)
+ obj = internal.createObject(null, {})
+ }
+
+ function destroyObject() {
+ setExpectNull(true)
+ obj.destroy();
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ property bool success: false
+
+ Component {
+ id: internal
+
+ Item {
+ }
+ }
+
+ property bool expectNull: null
+
+ function setExpectNull(b) {
+ success = false;
+ expectNull = b;
+ }
+
+ property variant obj: null
+ onObjChanged: success = (expectNull ? obj == null : obj != null)
+
+ Component.onCompleted: {
+ setExpectNull(false)
+ obj = internal.createObject(null, {})
+ if (!success) return
+
+ // Replace with a different object
+ setExpectNull(false)
+ obj = internal.createObject(null, {})
+ }
+
+ function destroyObject() {
+ setExpectNull(true)
+ obj.destroy();
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ property bool success: false
+
+ Component {
+ id: internal
+
+ Item {
+ }
+ }
+
+ property bool expectNull: null
+
+ function setExpectNull(b) {
+ success = false;
+ expectNull = b;
+ }
+
+ property var obj: null
+ onObjChanged: success = (expectNull ? obj == null : obj != null)
+
+ Component.onCompleted: {
+ setExpectNull(false)
+ obj = internal.createObject(null, {})
+ if (!success) return
+
+ // Replace with a different object
+ setExpectNull(false)
+ obj = internal.createObject(null, {})
+ }
+
+ function destroyObject() {
+ setExpectNull(true)
+ obj.destroy();
+ }
+}
--- /dev/null
+import QtQuick 2.0
+
+Item {
+ property bool success: false
+
+ Component {
+ id: internal
+
+ Item {
+ }
+ }
+
+ property var expectNull: null
+
+ function setExpectNull(b) {
+ success = false;
+ expectNull = b;
+ }
+
+ function setExpectNoChange() {
+ success = true;
+ expectNull = null;
+ }
+
+ property var obj: null
+ onObjChanged: success = (expectNull == null) ? false : (expectNull ? obj == null : obj != null)
+
+ property var temp: null
+
+ Component.onCompleted: {
+ // Set obj to contain an object
+ setExpectNull(false)
+ obj = internal.createObject(null, {})
+ if (!success) return
+
+ // Use temp variable to keep object reference alive
+ temp = obj
+
+ // Change obj to contain a string
+ setExpectNull(false)
+ obj = 'hello'
+ }
+
+ function destroyObject() {
+ setExpectNoChange()
+ temp.destroy();
+ }
+}
void literals_data();
void literals();
+ void objectDeletionNotify_data();
+ void objectDeletionNotify();
+
private:
QQmlEngine engine;
QStringList defaultImportPathList;
delete object;
}
+void tst_qqmllanguage::objectDeletionNotify_data()
+{
+ QTest::addColumn<QString>("file");
+
+ QTest::newRow("property QtObject") << "objectDeletionNotify.1.qml";
+ QTest::newRow("property variant") << "objectDeletionNotify.2.qml";
+ QTest::newRow("property var") << "objectDeletionNotify.3.qml";
+ QTest::newRow("property var guard removed") << "objectDeletionNotify.4.qml";
+}
+
+void tst_qqmllanguage::objectDeletionNotify()
+{
+ QFETCH(QString, file);
+
+ QQmlComponent component(&engine, testFile(file));
+
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ QCOMPARE(object->property("success").toBool(), true);
+
+ QMetaObject::invokeMethod(object, "destroyObject");
+
+ // Process the deletion event
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+
+ QCOMPARE(object->property("success").toBool(), true);
+
+ delete object;
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"