#include "qv8contextwrapper_p.h"
#include "qv8valuetypewrapper_p.h"
+#include "qv8gccallback_p.h"
#include "qv8include_p.h"
#include "../../../3rdparty/javascriptcore/DateMath.h"
v8::Context::Scope context_scope(m_context);
v8::V8::SetUserObjectComparisonCallbackFunction(ObjectComparisonCallback);
+ QV8GCCallback::registerGcPrologueCallback();
m_stringWrapper.init();
m_contextWrapper.init(this);
emit q->signalHandlerException(scriptValueFromInternal(uncaughtException()));
}
+QThreadStorage<QV8GCCallback::ThreadData *> QV8GCCallback::threadData;
+void QV8GCCallback::initializeThreadData()
+{
+ QV8GCCallback::ThreadData *newThreadData = new QV8GCCallback::ThreadData;
+ threadData.setLocalData(newThreadData);
+}
+
+void QV8GCCallback::registerGcPrologueCallback()
+{
+ if (!threadData.hasLocalData())
+ initializeThreadData();
+
+ QV8GCCallback::ThreadData *td = threadData.localData();
+ if (!td->gcPrologueCallbackRegistered) {
+ td->gcPrologueCallbackRegistered = true;
+ v8::V8::AddGCPrologueCallback(QV8GCCallback::garbageCollectorPrologueCallback, v8::kGCTypeMarkSweepCompact);
+ }
+}
+
+QV8GCCallback::Node::Node(PrologueCallback callback)
+ : prologueCallback(callback)
+{
+}
+
+QV8GCCallback::Node::~Node()
+{
+ node.remove();
+}
+
+QV8GCCallback::Referencer::Referencer()
+{
+ v8::HandleScope handleScope;
+ v8::Handle<v8::Context> context = v8::Context::New();
+ v8::Context::Scope contextScope(context);
+ strongReferencer = qPersistentNew(v8::Object::New());
+}
+
+void QV8GCCallback::Referencer::addRelationship(QObject *object, QObject *other)
+{
+ bool handleShouldBeStrong = false;
+ v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong);
+ v8::Persistent<v8::Value> handle = QDeclarativeData::get(other, true)->v8object;
+ if (handleShouldBeStrong) {
+ v8::V8::AddImplicitReferences(strongReferencer, &handle, 1);
+ } else if (!implicitOwner->IsEmpty()) {
+ v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1);
+ }
+}
+
+void QV8GCCallback::Referencer::addRelationship(QObject *object, v8::Persistent<v8::Value> handle)
+{
+ if (handle.IsEmpty())
+ return;
+
+ bool handleShouldBeStrong = false;
+ v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong);
+ if (handleShouldBeStrong) {
+ v8::V8::AddImplicitReferences(strongReferencer, &handle, 1);
+ } else if (!implicitOwner->IsEmpty()) {
+ v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1);
+ }
+}
+
+v8::Persistent<v8::Object> *QV8GCCallback::Referencer::findOwnerAndStrength(QObject *object, bool *shouldBeStrong)
+{
+ QObject *parent = object->parent();
+ if (!parent) {
+ // if the object has JS ownership, the object's v8object owns the lifetime of the persistent value.
+ if (QDeclarativeEngine::objectOwnership(object) == QDeclarativeEngine::JavaScriptOwnership) {
+ *shouldBeStrong = false;
+ return &(QDeclarativeData::get(object)->v8object);
+ }
+
+ // no parent, and has CPP ownership - doesn't have an implicit parent.
+ *shouldBeStrong = true;
+ return 0;
+ }
+
+ // if it is owned by CPP, it's root parent may still be owned by JS.
+ // in that case, the owner of the persistent handle is the root parent's v8object.
+ while (parent->parent())
+ parent = parent->parent();
+
+ if (QDeclarativeEngine::objectOwnership(parent) == QDeclarativeEngine::JavaScriptOwnership) {
+ // root parent is owned by JS. It's v8object owns the persistent value in question.
+ *shouldBeStrong = false;
+ return &(QDeclarativeData::get(parent)->v8object);
+ } else {
+ // root parent has CPP ownership. The persistent value should not be made weak.
+ *shouldBeStrong = true;
+ return 0;
+ }
+}
+
+/*
+ Ensure that each persistent handle is strong if it has CPP ownership
+ and has no implicitly JS owned object owner in its parent chain, and
+ weak otherwise.
+
+ Any weak handle whose parent object is still alive will have an implicit
+ reference (between the parent and the handle) added, so that it will
+ not be collected.
+
+ Note that this callback is registered only for kGCTypeMarkSweepCompact
+ collection cycles, as it is during collection cycles of that type
+ in which weak persistent handle callbacks are called when required.
+ */
+void QV8GCCallback::garbageCollectorPrologueCallback(v8::GCType, v8::GCCallbackFlags)
+{
+ if (!threadData.hasLocalData())
+ return;
+
+ QV8GCCallback::ThreadData *td = threadData.localData();
+ QV8GCCallback::Node *currNode = td->gcCallbackNodes.first();
+
+ while (currNode) {
+ // The client which adds itself to the list is responsible
+ // for maintaining the correct implicit references in the
+ // specified callback.
+ currNode->prologueCallback(&td->referencer, currNode);
+ currNode = td->gcCallbackNodes.next(currNode);
+ }
+}
+
+void QV8GCCallback::addGcCallbackNode(QV8GCCallback::Node *node)
+{
+ if (!threadData.hasLocalData())
+ initializeThreadData();
+
+ QV8GCCallback::ThreadData *td = threadData.localData();
+ td->gcCallbackNodes.insert(node);
+}
+
+QV8GCCallback::ThreadData::~ThreadData()
+{
+}
+
QT_END_NAMESPACE
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV8GCCALLBACK_P_H
+#define QV8GCCALLBACK_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobject.h>
+#include <QtCore/qthreadstorage.h>
+#include <private/qv8_p.h>
+#include <private/qintrusivelist_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QV8GCCallback
+{
+private:
+ class ThreadData;
+public:
+ static void garbageCollectorPrologueCallback(v8::GCType, v8::GCCallbackFlags);
+ static void registerGcPrologueCallback();
+
+ class Referencer {
+ public:
+ ~Referencer() {}
+ void addRelationship(QObject *object, v8::Persistent<v8::Value> handle);
+ void addRelationship(QObject *object, QObject *other);
+ private:
+ Referencer();
+ static v8::Persistent<v8::Object> *findOwnerAndStrength(QObject *qobjectOwner, bool *shouldBeStrong);
+ v8::Persistent<v8::Object> strongReferencer;
+ friend class QV8GCCallback::ThreadData;
+ };
+
+ class Node {
+ public:
+ typedef void (*PrologueCallback)(Referencer *r, Node *node);
+ Node(PrologueCallback callback);
+ ~Node();
+
+ QIntrusiveListNode node;
+ PrologueCallback prologueCallback;
+ };
+
+ static void addGcCallbackNode(Node *node);
+
+private:
+ class ThreadData {
+ public:
+ ThreadData() : gcPrologueCallbackRegistered(false) { }
+ ~ThreadData();
+ Referencer referencer;
+ bool gcPrologueCallbackRegistered;
+ QIntrusiveList<Node, &Node::node> gcCallbackNodes;
+ };
+
+ static void initializeThreadData();
+ static QThreadStorage<ThreadData *> threadData;
+};
+
+QT_END_NAMESPACE
+
+#endif // QV8GCCALLBACK_P_H
+
$$PWD/qv8debug_p.h \
$$PWD/qv8stringwrapper_p.h \
$$PWD/qv8engine_p.h \
+ $$PWD/qv8gccallback_p.h \
$$PWD/qv8contextwrapper_p.h \
$$PWD/qv8qobjectwrapper_p.h \
$$PWD/qv8typewrapper_p.h \
--- /dev/null
+import QtQuick 2.0
+import Qt.test 1.0
+
+Item {
+ id: obj
+ objectName: "obj"
+ property CircularReferenceHandle first
+ property CircularReferenceHandle second
+
+ CircularReferenceHandle {
+ id: crh
+ objectName: "crh"
+ }
+
+ function createReference() {
+ first = crh.generate(crh);
+ second = crh.generate(crh);
+ // NOTE: manually add reference from first to second
+ // in unit test prior reparenting and gc.
+ }
+
+ function performGc() {
+ gc();
+ }
+}
--- /dev/null
+import QtQuick 2.0
+import Qt.test 1.0
+
+Item {
+ id: obj
+ objectName: "obj"
+ property CircularReferenceHandle first
+ property CircularReferenceHandle second
+
+ CircularReferenceHandle {
+ id: crh
+ objectName: "crh"
+ }
+
+ function circularReference() {
+ // generate the circularly referential pair
+ first = crh.generate(crh);
+ second = crh.generate(crh);
+ // note: must manually reparent in unit test
+ // after setting the handle references.
+ }
+
+ function performGc() {
+ gc();
+ }
+}
--- /dev/null
+import QtQuick 2.0
+import Qt.test 1.0
+
+Item {
+ id: obj
+ objectName: "obj"
+
+ property CircularReferenceObject first
+ property CircularReferenceObject second
+
+
+ CircularReferenceObject {
+ id: cro
+ objectName: "cro"
+ }
+
+ function createReference() {
+ // generate the objects
+ first = cro.generate(cro); // has parent, so won't be collected
+ second = cro.generate(); // no parent, but will be kept alive by first's reference
+ first.addReference(second);
+
+ // remove top level references
+ first = cro;
+ second = cro;
+ }
+
+ function performGc() {
+ gc();
+ }
+}
--- /dev/null
+import QtQuick 2.0
+import Qt.test 1.0
+
+Item {
+ id: obj
+ objectName: "obj"
+
+ property CircularReferenceObject first
+ property CircularReferenceObject second
+
+
+ CircularReferenceObject {
+ id: cro
+ objectName: "cro"
+ }
+
+ function circularReference() {
+ // generate the circularly referential pair - they should still be collected
+ first = cro.generate(); // no parent, so should be collected
+ second = cro.generate(); // no parent, so should be collected
+ first.addReference(second);
+ second.addReference(first);
+
+ // remove top level references
+ first = cro;
+ second = cro;
+ }
+
+ function performGc() {
+ gc();
+ }
+}
qRegisterMetaType<MyQmlObject::MyType>("MyEnum2");
qRegisterMetaType<Qt::MouseButtons>("Qt::MouseButtons");
+
+ qmlRegisterType<CircularReferenceObject>("Qt.test", 1, 0, "CircularReferenceObject");
+ qmlRegisterType<CircularReferenceHandle>("Qt.test", 1, 0, "CircularReferenceHandle");
}
#include "testtypes.moc"
#include <QtDeclarative/qdeclarativescriptstring.h>
#include <QtDeclarative/qdeclarativecomponent.h>
+#include <private/qv8gccallback_p.h>
+#include <private/qdeclarativeengine_p.h>
+
class MyQmlAttachedObject : public QObject
{
Q_OBJECT
int m_methodCallCount;
};
+class CircularReferenceObject : public QObject,
+ public QV8GCCallback::Node
+{
+ Q_OBJECT
+
+public:
+ CircularReferenceObject(QObject *parent = 0)
+ : QObject(parent), QV8GCCallback::Node(callback), m_referenced(0), m_dtorCount(0)
+ {
+ QV8GCCallback::addGcCallbackNode(this);
+ }
+
+ ~CircularReferenceObject()
+ {
+ if (m_dtorCount) *m_dtorCount = *m_dtorCount + 1;
+ }
+
+ Q_INVOKABLE void setDtorCount(int *dtorCount)
+ {
+ m_dtorCount = dtorCount;
+ }
+
+ Q_INVOKABLE CircularReferenceObject *generate(QObject *parent = 0)
+ {
+ CircularReferenceObject *retn = new CircularReferenceObject(parent);
+ retn->m_dtorCount = m_dtorCount;
+ return retn;
+ }
+
+ Q_INVOKABLE void addReference(QObject *other)
+ {
+ m_referenced = other;
+ }
+
+ static void callback(QV8GCCallback::Referencer *r, QV8GCCallback::Node *n)
+ {
+ CircularReferenceObject *cro = static_cast<CircularReferenceObject*>(n);
+ if (cro->m_referenced) {
+ r->addRelationship(cro, cro->m_referenced);
+ }
+ }
+
+private:
+ QObject *m_referenced;
+ int *m_dtorCount;
+};
+Q_DECLARE_METATYPE(CircularReferenceObject*)
+
+class CircularReferenceHandle : public QObject,
+ public QV8GCCallback::Node
+{
+ Q_OBJECT
+
+public:
+ CircularReferenceHandle(QObject *parent = 0)
+ : QObject(parent), QV8GCCallback::Node(gccallback), m_dtorCount(0)
+ {
+ QV8GCCallback::addGcCallbackNode(this);
+ }
+
+ ~CircularReferenceHandle()
+ {
+ if (m_dtorCount) *m_dtorCount = *m_dtorCount + 1;
+ }
+
+ Q_INVOKABLE void setDtorCount(int *dtorCount)
+ {
+ m_dtorCount = dtorCount;
+ }
+
+ Q_INVOKABLE CircularReferenceHandle *generate(QObject *parent = 0)
+ {
+ CircularReferenceHandle *retn = new CircularReferenceHandle(parent);
+ retn->m_dtorCount = m_dtorCount;
+ return retn;
+ }
+
+ Q_INVOKABLE void addReference(v8::Persistent<v8::Value> handle)
+ {
+ m_referenced = qPersistentNew(handle);
+ m_referenced.MakeWeak(static_cast<void*>(this), wrcallback);
+ }
+
+ static void wrcallback(v8::Persistent<v8::Value> handle, void *params)
+ {
+ CircularReferenceHandle *crh = static_cast<CircularReferenceHandle*>(params);
+ qPersistentDispose(handle);
+ crh->m_referenced.Clear();
+ }
+
+ static void gccallback(QV8GCCallback::Referencer *r, QV8GCCallback::Node *n)
+ {
+ CircularReferenceHandle *crh = static_cast<CircularReferenceHandle*>(n);
+ r->addRelationship(crh, crh->m_referenced);
+ }
+
+private:
+ v8::Persistent<v8::Value> m_referenced;
+ int *m_dtorCount;
+};
+Q_DECLARE_METATYPE(CircularReferenceHandle*)
+
void registerTypes();
#endif // TESTTYPES_H
void elementAssign();
void objectPassThroughSignals();
void booleanConversion();
+ void handleReferenceManagement();
void bug1();
void bug2();
delete object;
}
+void tst_qdeclarativeecmascript::handleReferenceManagement()
+{
+
+ int dtorCount = 0;
+ {
+ // Linear QObject reference
+ QDeclarativeEngine hrmEngine;
+ QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.object.1.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
+ cro->setDtorCount(&dtorCount);
+ QMetaObject::invokeMethod(object, "createReference");
+ QMetaObject::invokeMethod(object, "performGc");
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
+ delete object;
+ hrmEngine.collectGarbage();
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 3);
+ }
+
+ dtorCount = 0;
+ {
+ // Circular QObject reference
+ QDeclarativeEngine hrmEngine;
+ QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.object.2.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
+ cro->setDtorCount(&dtorCount);
+ QMetaObject::invokeMethod(object, "circularReference");
+ QMetaObject::invokeMethod(object, "performGc");
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
+ delete object;
+ hrmEngine.collectGarbage();
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 3);
+ }
+
+ dtorCount = 0;
+ {
+ // Linear handle reference
+ QDeclarativeEngine hrmEngine;
+ QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
+ QVERIFY(crh != 0);
+ crh->setDtorCount(&dtorCount);
+ QMetaObject::invokeMethod(object, "createReference");
+ CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
+ CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
+ QVERIFY(first != 0);
+ QVERIFY(second != 0);
+ first->addReference(QDeclarativeData::get(second)->v8object); // create reference
+ // now we have to reparent second and make second owned by JS.
+ second->setParent(0);
+ QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
+ QMetaObject::invokeMethod(object, "performGc");
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
+ delete object;
+ hrmEngine.collectGarbage();
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 3);
+ }
+
+ dtorCount = 0;
+ {
+ // Circular handle reference
+ QDeclarativeEngine hrmEngine;
+ QDeclarativeComponent component(&hrmEngine, TEST_FILE("handleReferenceManagement.handle.2.qml"));
+ QObject *object = component.create();
+ QVERIFY(object != 0);
+ CircularReferenceHandle *crh = object->findChild<CircularReferenceHandle*>("crh");
+ QVERIFY(crh != 0);
+ crh->setDtorCount(&dtorCount);
+ QMetaObject::invokeMethod(object, "circularReference");
+ CircularReferenceHandle *first = object->property("first").value<CircularReferenceHandle*>();
+ CircularReferenceHandle *second = object->property("second").value<CircularReferenceHandle*>();
+ QVERIFY(first != 0);
+ QVERIFY(second != 0);
+ first->addReference(QDeclarativeData::get(second)->v8object); // create circular reference
+ second->addReference(QDeclarativeData::get(first)->v8object); // note: must be weak.
+ // now we have to reparent and change ownership.
+ first->setParent(0);
+ second->setParent(0);
+ QDeclarativeEngine::setObjectOwnership(first, QDeclarativeEngine::JavaScriptOwnership);
+ QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
+ QMetaObject::invokeMethod(object, "performGc");
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
+ delete object;
+ hrmEngine.collectGarbage();
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 3);
+ }
+
+ dtorCount = 0;
+ {
+ // multiple engine interaction - linear reference
+ QDeclarativeEngine hrmEngine1;
+ QDeclarativeEngine hrmEngine2;
+ QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+ QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+ QObject *object1 = component1.create();
+ QObject *object2 = component2.create();
+ QVERIFY(object1 != 0);
+ QVERIFY(object2 != 0);
+ CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
+ CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
+ QVERIFY(crh1 != 0);
+ QVERIFY(crh2 != 0);
+ crh1->setDtorCount(&dtorCount);
+ crh2->setDtorCount(&dtorCount);
+ QMetaObject::invokeMethod(object1, "createReference");
+ QMetaObject::invokeMethod(object2, "createReference");
+ CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
+ CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
+ CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
+ CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
+ QVERIFY(first1 != 0);
+ QVERIFY(second1 != 0);
+ QVERIFY(first2 != 0);
+ QVERIFY(second2 != 0);
+ first1->addReference(QDeclarativeData::get(second2)->v8object); // create reference across engines
+ // now we have to reparent second2 and make second2 owned by JS.
+ second2->setParent(0);
+ QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
+ QMetaObject::invokeMethod(object1, "performGc");
+ QMetaObject::invokeMethod(object2, "performGc");
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
+ delete object1;
+ delete object2;
+ hrmEngine1.collectGarbage();
+ hrmEngine2.collectGarbage();
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 6);
+ }
+
+ dtorCount = 0;
+ {
+ // multiple engine interaction - circular reference
+ QDeclarativeEngine hrmEngine1;
+ QDeclarativeEngine hrmEngine2;
+ QDeclarativeComponent component1(&hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+ QDeclarativeComponent component2(&hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+ QObject *object1 = component1.create();
+ QObject *object2 = component2.create();
+ QVERIFY(object1 != 0);
+ QVERIFY(object2 != 0);
+ CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
+ CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
+ QVERIFY(crh1 != 0);
+ QVERIFY(crh2 != 0);
+ crh1->setDtorCount(&dtorCount);
+ crh2->setDtorCount(&dtorCount);
+ QMetaObject::invokeMethod(object1, "createReference");
+ QMetaObject::invokeMethod(object2, "createReference");
+ CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
+ CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
+ CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
+ CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
+ QVERIFY(first1 != 0);
+ QVERIFY(second1 != 0);
+ QVERIFY(first2 != 0);
+ QVERIFY(second2 != 0);
+ first1->addReference(QDeclarativeData::get(second1)->v8object); // create linear reference within engine1
+ second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines
+ second2->addReference(QDeclarativeData::get(first2)->v8object); // create linear reference within engine2
+ first2->addReference(QDeclarativeData::get(first1)->v8object); // close the loop - circular ref across engines
+ // now we have to reparent and change ownership to JS.
+ first1->setParent(0);
+ second1->setParent(0);
+ first2->setParent(0);
+ second2->setParent(0);
+ QDeclarativeEngine::setObjectOwnership(first1, QDeclarativeEngine::JavaScriptOwnership);
+ QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
+ QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
+ QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
+ QMetaObject::invokeMethod(object1, "performGc");
+ QMetaObject::invokeMethod(object2, "performGc");
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
+ delete object1;
+ delete object2;
+ hrmEngine1.collectGarbage();
+ hrmEngine2.collectGarbage();
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 6);
+ }
+
+ dtorCount = 0;
+ {
+ // multiple engine interaction - linear reference with engine deletion
+ QDeclarativeEngine *hrmEngine1 = new QDeclarativeEngine;
+ QDeclarativeEngine *hrmEngine2 = new QDeclarativeEngine;
+ QDeclarativeComponent component1(hrmEngine1, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+ QDeclarativeComponent component2(hrmEngine2, TEST_FILE("handleReferenceManagement.handle.1.qml"));
+ QObject *object1 = component1.create();
+ QObject *object2 = component2.create();
+ QVERIFY(object1 != 0);
+ QVERIFY(object2 != 0);
+ CircularReferenceHandle *crh1 = object1->findChild<CircularReferenceHandle*>("crh");
+ CircularReferenceHandle *crh2 = object2->findChild<CircularReferenceHandle*>("crh");
+ QVERIFY(crh1 != 0);
+ QVERIFY(crh2 != 0);
+ crh1->setDtorCount(&dtorCount);
+ crh2->setDtorCount(&dtorCount);
+ QMetaObject::invokeMethod(object1, "createReference");
+ QMetaObject::invokeMethod(object2, "createReference");
+ CircularReferenceHandle *first1 = object1->property("first").value<CircularReferenceHandle*>();
+ CircularReferenceHandle *second1 = object1->property("second").value<CircularReferenceHandle*>();
+ CircularReferenceHandle *first2 = object2->property("first").value<CircularReferenceHandle*>();
+ CircularReferenceHandle *second2 = object2->property("second").value<CircularReferenceHandle*>();
+ QVERIFY(first1 != 0);
+ QVERIFY(second1 != 0);
+ QVERIFY(first2 != 0);
+ QVERIFY(second2 != 0);
+ first1->addReference(QDeclarativeData::get(second1)->v8object); // create linear reference within engine1
+ second1->addReference(QDeclarativeData::get(second2)->v8object); // create linear reference across engines
+ second2->addReference(QDeclarativeData::get(first2)->v8object); // create linear reference within engine2
+ // now we have to reparent and change ownership to JS.
+ first1->setParent(crh1);
+ second1->setParent(0);
+ first2->setParent(0);
+ second2->setParent(0);
+ QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
+ QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
+ QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
+ QMetaObject::invokeMethod(object1, "performGc");
+ QMetaObject::invokeMethod(object2, "performGc");
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 0);
+ delete hrmEngine2;
+ QMetaObject::invokeMethod(object1, "performGc");
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 0);
+ delete object1;
+ delete object2;
+ hrmEngine1->collectGarbage();
+ QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+ QCOMPARE(dtorCount, 6);
+ delete hrmEngine1;
+ }
+}
+
// Test that assigning a null object works
// Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
void tst_qdeclarativeecmascript::nullObjectBinding()