Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Ensure JS-owned QObjects are cleaned up on v8engine dtor
This commit ensures that the garbage collector is invoked during
engine destruction.  This commit also adds a unit test which
ensures that the JS GC destroys JS-owned C++ objects correctly
when the QDeclarativeEngine is destroyed.

Task-number: QTBUG-20377
Change-Id: I2de1f2dfd1e60cc2f76abb523b99bf169d2a5a13
Reviewed-on: http://codereview.qt-project.org/3285
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>
  • Loading branch information
Chris Adams authored and Qt by Nokia committed Sep 30, 2011
1 parent bc2f2c5 commit 5d62a90
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/declarative/qml/qdeclarativeengine.cpp
Expand Up @@ -531,6 +531,9 @@ QDeclarativeEngine::~QDeclarativeEngine()
d->moduleApiInstances.remove(key);
}
}

// ensure we clean up QObjects with JS ownership
d->v8engine()->gc();
}

/*! \fn void QDeclarativeEngine::quit()
Expand Down
@@ -0,0 +1,20 @@
import QtQuick 2.0
import Qt.test 1.0

Item {
id: obj
objectName: "obj"

MyDynamicCreationDestructionObject {
id: mdcdo
objectName: "mdcdo"
}

function dynamicallyCreateJsOwnedObject() {
mdcdo.createNew();
}

function performGc() {
gc();
}
}
2 changes: 2 additions & 0 deletions tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
Expand Up @@ -192,6 +192,8 @@ void registerTypes()

qmlRegisterType<CircularReferenceObject>("Qt.test", 1, 0, "CircularReferenceObject");
qmlRegisterType<CircularReferenceHandle>("Qt.test", 1, 0, "CircularReferenceHandle");

qmlRegisterType<MyDynamicCreationDestructionObject>("Qt.test", 1, 0, "MyDynamicCreationDestructionObject");
}

#include "testtypes.moc"
41 changes: 41 additions & 0 deletions tests/auto/declarative/qdeclarativeecmascript/testtypes.h
Expand Up @@ -1072,6 +1072,47 @@ class CircularReferenceHandle : public QObject,
};
Q_DECLARE_METATYPE(CircularReferenceHandle*)

class MyDynamicCreationDestructionObject : public QObject
{
Q_OBJECT
Q_PROPERTY (int intProperty READ intProperty WRITE setIntProperty NOTIFY intPropertyChanged)

public:
MyDynamicCreationDestructionObject(QObject *parent = 0) : QObject(parent), m_intProperty(0), m_dtorCount(0)
{
}

~MyDynamicCreationDestructionObject()
{
if (m_dtorCount) {
(*m_dtorCount)++;
}
}

int intProperty() const { return m_intProperty; }
void setIntProperty(int val) { m_intProperty = val; emit intPropertyChanged(); }

Q_INVOKABLE MyDynamicCreationDestructionObject *createNew()
{
// no parent == ownership transfers to JS; same dtor counter.
MyDynamicCreationDestructionObject *retn = new MyDynamicCreationDestructionObject;
retn->setDtorCount(m_dtorCount);
return retn;
}

void setDtorCount(int *dtorCount)
{
m_dtorCount = dtorCount;
}

signals:
void intPropertyChanged();

private:
int m_intProperty;
int *m_dtorCount;
};

void registerTypes();

#endif // TESTTYPES_H
Expand Down
Expand Up @@ -164,6 +164,7 @@ private slots:
void bug1();
void bug2();
void dynamicCreationCrash();
void dynamicCreationOwnership();
void regExpBug();
void nullObjectBinding();
void deletedEngine();
Expand Down Expand Up @@ -1664,6 +1665,40 @@ void tst_qdeclarativeecmascript::dynamicCreationCrash()
delete object;
}

// ownership transferred to JS, ensure that GC runs the dtor
void tst_qdeclarativeecmascript::dynamicCreationOwnership()
{
int dtorCount = 0;
int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.

// allow the engine to go out of scope too.
{
QDeclarativeEngine dcoEngine;
QDeclarativeComponent component(&dcoEngine, TEST_FILE("dynamicCreationOwnership.qml"));
QObject *object = component.create();
QVERIFY(object != 0);
MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>("mdcdo");
QVERIFY(mdcdo != 0);
mdcdo->setDtorCount(&dtorCount);

for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
QMetaObject::invokeMethod(object, "dynamicallyCreateJsOwnedObject");
if (i % 90 == 0) {
// we do this once manually, but it should be done automatically
// when the engine goes out of scope (since it should gc in dtor)
QMetaObject::invokeMethod(object, "performGc");
}
if (i % 10 == 0) {
QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
}
}

delete object;
}
QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
QCOMPARE(dtorCount, expectedDtorCount);
}

//QTBUG-9367
void tst_qdeclarativeecmascript::regExpBug()
{
Expand Down

0 comments on commit 5d62a90

Please sign in to comment.