Fix tst_qqmlecmascript::signalEmitted
authorSimon Hausmann <simon.hausmann@digia.com>
Fri, 7 Jun 2013 13:59:55 +0000 (15:59 +0200)
committerLars Knoll <lars.knoll@digia.com>
Fri, 7 Jun 2013 16:33:39 +0000 (18:33 +0200)
This test executes code in the Component.onDestruction signal handler that ends
up doing things like allocating new V4 objects on the GC heap. Since Component.onDestruction
is called (indirectly) from QV4::QObjectWrapper::~QObjectWrapper(), we have a slight
problem:

From within the destructor of a garbage collected object, _during_ a sweep, we
end up trying to allocate new managed objects. Instead of adding more
complexity to the memory manager, this patch avoids doing clever things in the
destructor, by delaying the emission of the destructed signal to the point
right before the real deletion of the QObject (which was already delayed with a
deleteLater).

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

index 9d99954..2cd4767 100644 (file)
@@ -264,6 +264,27 @@ void QObjectWrapper::initializeBindings(ExecutionEngine *engine)
     engine->functionPrototype->defineDefaultProperty(engine, QStringLiteral("disconnect"), method_disconnect);
 }
 
+namespace {
+    struct SafeQMLObjectDeleter : public QObject
+    {
+        SafeQMLObjectDeleter(QObject *objectToDelete)
+            : m_objectToDelete(objectToDelete)
+        {}
+
+        ~SafeQMLObjectDeleter()
+        {
+            QQmlData *ddata = QQmlData::get(m_objectToDelete, false);
+            if (ddata && ddata->ownContext && ddata->context)
+                ddata->context->emitDestruction();
+            // This object is notionally destroyed now
+            ddata->isQueuedForDeletion = true;
+            delete m_objectToDelete;
+        }
+
+        QObject *m_objectToDelete;
+    };
+}
+
 void QObjectWrapper::deleteQObject(bool deleteInstantly)
 {
     if (!m_object)
@@ -272,14 +293,18 @@ void QObjectWrapper::deleteQObject(bool deleteInstantly)
     if (!ddata)
         return;
     if (!m_object->parent() && !ddata->indestructible) {
-        // This object is notionally destroyed now
-        if (ddata->ownContext && ddata->context)
-            ddata->context->emitDestruction();
-        ddata->isQueuedForDeletion = true;
-        if (deleteInstantly)
+        if (deleteInstantly) {
+            QQmlData *ddata = QQmlData::get(m_object, false);
+            if (ddata->ownContext && ddata->context)
+                ddata->context->emitDestruction();
+            // This object is notionally destroyed now
+            ddata->isQueuedForDeletion = true;
             delete m_object;
-        else
-            m_object->deleteLater();
+        } else {
+            QObject *deleter = new SafeQMLObjectDeleter(m_object);
+            deleter->deleteLater();
+            m_object = 0;
+        }
     }
 }
 
@@ -581,7 +606,7 @@ Value QObjectWrapper::wrap(ExecutionEngine *engine, QObject *object)
         return QV4::Value::undefinedValue();
 
     if (ddata->jsEngineId == engine->m_engineId && !ddata->jsWrapper.isEmpty()) {
-        // We own the v8object
+        // We own the JS object
         return ddata->jsWrapper.value();
     } else if (ddata->jsWrapper.isEmpty() &&
                (ddata->jsEngineId == engine->m_engineId || // We own the QObject