Fix JS ownership of children of marked but floating QObjects
authorSimon Hausmann <simon.hausmann@digia.com>
Fri, 14 Jun 2013 10:21:45 +0000 (12:21 +0200)
committerLars Knoll <lars.knoll@digia.com>
Sat, 15 Jun 2013 13:23:44 +0000 (15:23 +0200)
This patch fixes four qqmlecmascript tests: propertyVarImplicitOwnership,
propertyVarReparent, propertyVarReparentNullContext, propertyVarCircular2

Usually QObjects wrapped in JS are kept alive because the GC knows about their
weak values stored in the QQmlData, and so it has to do few steps only to
locate those and mark them.

A slightly less common case is a QObject that's wrapped in JS, without a parent
and is stored in a var or variant property. It's visible to the GC, but the
only strong reference to it is through that var property. So when such an
object gets marked (it's without parent), we also have to mark the child
QObjects's JS wrappers (if existent), all recursively.

Change-Id: Ice347a37a841212c145b6d7a58216e449e5cce08
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/qml/qml/v4/qv4qobjectwrapper.cpp

index 5da472b..087458d 100644 (file)
@@ -856,13 +856,34 @@ Value QObjectWrapper::method_disconnect(SimpleCallContext *ctx)
     return QV4::Value::undefinedValue();
 }
 
+static void markChildQObjectsRecursively(QObject *parent)
+{
+    const QObjectList &children = parent->children();
+    for (int i = 0; i < children.count(); ++i) {
+        QObject *child = children.at(i);
+        QQmlData *ddata = QQmlData::get(child, /*create*/false);
+        if (ddata)
+            ddata->jsWrapper.markOnce();
+        markChildQObjectsRecursively(child);
+    }
+}
+
 void QObjectWrapper::markObjects(Managed *that)
 {
     QObjectWrapper *This = static_cast<QObjectWrapper*>(that);
 
-    QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(This->m_object);
-    if (vme)
-        vme->mark();
+    if (QObject *o = This->m_object.data()) {
+        QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o);
+        if (vme)
+            vme->mark();
+
+        // Children usually don't need to be marked, the gc keeps them alive.
+        // But in the rare case of a "floating" QObject without a parent that
+        // _gets_ marked (we've been called here!) then we also need to
+        // propagate the marking down to the children recursively.
+        if (!o->parent())
+            markChildQObjectsRecursively(o);
+    }
 
     QV4::Object::markObjects(that);
 }