Skip to content

Commit

Permalink
Prevent the root object from being garbage collected.
Browse files Browse the repository at this point in the history
Passing the root object as a return value from a C++ function could
cause the indestructible flag to be set to false.

Change-Id: Ib70c666f0d0ffbb48bca1996c2517fbccafa5dc1
Reviewed-by: Chris Adams <christopher.adams@nokia.com>
  • Loading branch information
Michael Brasser authored and Qt by Nokia committed Mar 23, 2012
1 parent b63ce68 commit 91f9f12
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 8 deletions.
10 changes: 5 additions & 5 deletions src/qml/qml/qqmlcomponent.cpp
Expand Up @@ -829,7 +829,10 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context)
if (rv) {
QQmlData *ddata = QQmlData::get(rv);
Q_ASSERT(ddata);
//top level objects should never get JS ownership.
//if JS ownership is needed this needs to be explicitly undone (like in component.createObject())
ddata->indestructible = true;
ddata->explicitIndestructibleSet = true;
}

if (enginePriv->isDebugging && rv) {
Expand Down Expand Up @@ -1120,7 +1123,8 @@ void QQmlComponent::createObject(QQmlV8Function *args)
d->completeCreate();

Q_ASSERT(QQmlData::get(rv));
QQmlData::get(rv)->setImplicitDestructible();
QQmlData::get(rv)->explicitIndestructibleSet = false;
QQmlData::get(rv)->indestructible = false;

if (!rv)
args->returnValue(v8::Null());
Expand Down Expand Up @@ -1255,10 +1259,6 @@ void QQmlComponentPrivate::initializeObjectWithInitialProperties(v8::Handle<v8::
v8::Handle<v8::Value> args[] = { object, valuemap };
v8::Handle<v8::Function>::Cast(function)->Call(v8engine->global(), 2, args);
}

QQmlData *ddata = QQmlData::get(toCreate);
Q_ASSERT(ddata);
ddata->setImplicitDestructible();
}


Expand Down
3 changes: 2 additions & 1 deletion src/qml/qml/qqmlincubator.cpp
Expand Up @@ -293,8 +293,9 @@ void QQmlIncubatorPrivate::incubate(QQmlVME::Interrupt &i)
if (result) {
QQmlData *ddata = QQmlData::get(result);
Q_ASSERT(ddata);
//see QQmlComponent::beginCreate for explanation of indestructible
ddata->indestructible = true;

ddata->explicitIndestructibleSet = true;
q->setInitialState(result);
}

Expand Down
8 changes: 6 additions & 2 deletions src/qml/qml/v8/qqmlbuiltinfunctions.cpp
Expand Up @@ -1097,7 +1097,9 @@ v8::Handle<v8::Value> createQmlObject(const v8::Arguments &args)

QObject *obj = component.beginCreate(effectiveContext);
if (obj) {
QQmlData::get(obj, true)->setImplicitDestructible();
QQmlData::get(obj, true)->explicitIndestructibleSet = false;
QQmlData::get(obj)->indestructible = false;


obj->setParent(parentArg);

Expand Down Expand Up @@ -1208,7 +1210,9 @@ v8::Handle<v8::Value> createComponent(const v8::Arguments &args)
QUrl url = context->resolvedUrl(QUrl(arg));
QQmlComponent *c = new QQmlComponent(engine, url, compileMode, parentArg);
QQmlComponentPrivate::get(c)->creationContext = effectiveContext;
QQmlData::get(c, true)->setImplicitDestructible();
QQmlData::get(c, true)->explicitIndestructibleSet = false;
QQmlData::get(c)->indestructible = false;

return v8engine->newQObject(c);
}

Expand Down
21 changes: 21 additions & 0 deletions tests/auto/qml/qqmlecmascript/data/ownershipConsistency.qml
@@ -0,0 +1,21 @@
import QtQuick 2.0

Item {
Loader {
source: "PropertyVarBaseItem.qml"
onLoaded: item.destroy()
}
Loader {
Component.onCompleted: setSource("PropertyVarBaseItem.qml", { random: "" })
onLoaded: item.destroy()
}

Repeater {
model: 1
Item { Component.onCompleted: destroy() }
}
Repeater {
model: 1
Item { id: me; Component.onCompleted: { setObject(me); getObject().destroy() } }
}
}
6 changes: 6 additions & 0 deletions tests/auto/qml/qqmlecmascript/data/ownershipRootObject.qml
@@ -0,0 +1,6 @@
import QtQuick 2.0

QtObject {
id: root
Component.onCompleted: { setObject(root); getObject() }
}
68 changes: 68 additions & 0 deletions tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
Expand Up @@ -133,6 +133,8 @@ private slots:
void ownership();
void cppOwnershipReturnValue();
void ownershipCustomReturnValue();
void ownershipRootObject();
void ownershipConsistency();
void qlistqobjectMethods();
void strictlyEquals();
void compiled();
Expand Down Expand Up @@ -2939,6 +2941,72 @@ void tst_qqmlecmascript::ownershipCustomReturnValue()
QVERIFY(source.value == 0);
}

//the return value from getObject will be JS ownership,
//unless strong Cpp ownership has been set
class OwnershipChangingObject : public QObject
{
Q_OBJECT
public:
OwnershipChangingObject(): object(0) { }

QPointer<QObject> object;

public slots:
QObject *getObject() { return object; }
void setObject(QObject *obj) { object = obj; }
};

void tst_qqmlecmascript::ownershipRootObject()
{
OwnershipChangingObject own;
QQmlContext *context = new QQmlContext(engine.rootContext());
context->setContextObject(&own);

QQmlComponent component(&engine, testFileUrl("ownershipRootObject.qml"));
QQmlGuard<QObject> object = component.create(context);
QVERIFY(object);

engine.collectGarbage();

QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
QCoreApplication::processEvents();

QVERIFY(own.object != 0);

delete context;
delete object;
}

void tst_qqmlecmascript::ownershipConsistency()
{
OwnershipChangingObject own;
QQmlContext *context = new QQmlContext(engine.rootContext());
context->setContextObject(&own);

QString expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":19: Error: Invalid attempt to destroy() an indestructible object");
QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":15: Error: Invalid attempt to destroy() an indestructible object");
QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":6: Error: Invalid attempt to destroy() an indestructible object");
QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
expectedWarning = testFileUrl("ownershipConsistency.qml").toString() + QLatin1String(":10: Error: Invalid attempt to destroy() an indestructible object");
QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.

QQmlComponent component(&engine, testFileUrl("ownershipConsistency.qml"));
QQmlGuard<QObject> object = component.create(context);
QVERIFY(object);

engine.collectGarbage();

QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
QCoreApplication::processEvents();

QVERIFY(own.object != 0);

delete context;
delete object;
}

class QListQObjectMethodsObject : public QObject
{
Q_OBJECT
Expand Down

0 comments on commit 91f9f12

Please sign in to comment.