aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/qml/qqmlcontext.cpp54
-rw-r--r--src/qml/qml/qqmlcontext_p.h4
-rw-r--r--src/qml/qml/qqmlengine.cpp5
-rw-r--r--src/qml/qml/v8/qv8qobjectwrapper.cpp3
-rw-r--r--tests/auto/qml/qqmlcomponent/data/onDestructionCount.qml16
-rw-r--r--tests/auto/qml/qqmlcomponent/data/onDestructionLookup.qml25
-rw-r--r--tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp43
-rw-r--r--tests/auto/qml/qqmlecmascript/data/OnDestructionComponent.qml9
-rw-r--r--tests/auto/qml/qqmlecmascript/data/onDestruction.qml18
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.h2
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp26
11 files changed, 182 insertions, 23 deletions
diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp
index cbb8430f40..7e15993a65 100644
--- a/src/qml/qml/qqmlcontext.cpp
+++ b/src/qml/qml/qqmlcontext.cpp
@@ -513,7 +513,8 @@ QObject *QQmlContextPrivate::context_at(QQmlListProperty<QObject> *prop, int ind
QQmlContextData::QQmlContextData()
: parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
- isPragmaLibraryContext(false), unresolvedNames(false), publicContext(0), activeVMEData(0),
+ isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false),
+ publicContext(0), activeVMEData(0),
propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0),
componentAttached(0), v4bindings(0), v8bindings(0)
@@ -522,25 +523,45 @@ QQmlContextData::QQmlContextData()
QQmlContextData::QQmlContextData(QQmlContext *ctxt)
: parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false),
- isPragmaLibraryContext(false), unresolvedNames(false), publicContext(ctxt), activeVMEData(0),
+ isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false),
+ publicContext(ctxt), activeVMEData(0),
propertyNames(0), contextObject(0), imports(0), childContexts(0), nextChild(0), prevChild(0),
expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0),
componentAttached(0), v4bindings(0), v8bindings(0)
{
}
-void QQmlContextData::invalidate()
+void QQmlContextData::emitDestruction()
{
- while (componentAttached) {
- QQmlComponentAttached *a = componentAttached;
- componentAttached = a->next;
- if (componentAttached) componentAttached->prev = &componentAttached;
+ if (!hasEmittedDestruction) {
+ hasEmittedDestruction = true;
+
+ // Emit the destruction signal - must be emitted before invalidate so that the
+ // context is still valid if bindings or resultant expression evaluation requires it
+ if (engine) {
+ while (componentAttached) {
+ QQmlComponentAttached *a = componentAttached;
+ componentAttached = a->next;
+ if (componentAttached) componentAttached->prev = &componentAttached;
- a->next = 0;
- a->prev = 0;
+ a->next = 0;
+ a->prev = 0;
+
+ emit a->destruction();
+ }
- emit a->destruction();
+ QQmlContextData * child = childContexts;
+ while (child) {
+ child->emitDestruction();
+ child = child->nextChild;
+ }
+ }
}
+}
+
+void QQmlContextData::invalidate()
+{
+ emitDestruction();
while (childContexts) {
if (childContexts->ownedByParent) {
@@ -563,18 +584,7 @@ void QQmlContextData::invalidate()
void QQmlContextData::clearContext()
{
- if (engine) {
- while (componentAttached) {
- QQmlComponentAttached *a = componentAttached;
- componentAttached = a->next;
- if (componentAttached) componentAttached->prev = &componentAttached;
-
- a->next = 0;
- a->prev = 0;
-
- emit a->destruction();
- }
- }
+ emitDestruction();
QQmlAbstractExpression *expression = expressions;
while (expression) {
diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h
index 97bc04b91d..1f1c6011a2 100644
--- a/src/qml/qml/qqmlcontext_p.h
+++ b/src/qml/qml/qqmlcontext_p.h
@@ -118,6 +118,7 @@ class Q_QML_EXPORT QQmlContextData
public:
QQmlContextData();
QQmlContextData(QQmlContext *);
+ void emitDestruction();
void clearContext();
void destroy();
void invalidate();
@@ -146,7 +147,8 @@ public:
quint32 isJSContext:1;
quint32 isPragmaLibraryContext:1;
quint32 unresolvedNames:1; // True if expressions in this context failed to resolve a toplevel name
- quint32 dummy:27;
+ quint32 hasEmittedDestruction:1;
+ quint32 dummy:26;
QQmlContext *publicContext;
// VME data that is constructing this context if any
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index c79e756147..2717ed3a08 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -575,6 +575,11 @@ QQmlEngine::~QQmlEngine()
QQmlEngineDebugService::instance()->remEngine(this);
}
+ // Emit onDestruction signals for the root context before
+ // we destroy the contexts, engine, Module APIs etc. that
+ // may be required to handle the destruction signal.
+ QQmlContextData::get(rootContext())->emitDestruction();
+
// if we are the parent of any of the qobject module api instances,
// we need to remove them from our internal list, in order to prevent
// a segfault in engine private dtor.
diff --git a/src/qml/qml/v8/qv8qobjectwrapper.cpp b/src/qml/qml/v8/qv8qobjectwrapper.cpp
index f2acac5bbc..7e7b6eb570 100644
--- a/src/qml/qml/v8/qv8qobjectwrapper.cpp
+++ b/src/qml/qml/v8/qv8qobjectwrapper.cpp
@@ -1160,6 +1160,9 @@ void QV8QObjectWrapper::deleteWeakQObject(QV8QObjectResource *resource)
ddata->v8object.Clear();
if (!object->parent() && !ddata->indestructible) {
+ // This object is notionally destroyed now
+ if (ddata->ownContext && ddata->context)
+ ddata->context->emitDestruction();
ddata->isQueuedForDeletion = true;
object->deleteLater();
}
diff --git a/tests/auto/qml/qqmlcomponent/data/onDestructionCount.qml b/tests/auto/qml/qqmlcomponent/data/onDestructionCount.qml
new file mode 100644
index 0000000000..3938acf6a5
--- /dev/null
+++ b/tests/auto/qml/qqmlcomponent/data/onDestructionCount.qml
@@ -0,0 +1,16 @@
+import QtQuick 2.0
+
+Item {
+ Component {
+ id: internalComponent
+
+ Item {
+ Component.onDestruction: console.warn('Component.onDestruction')
+ }
+ }
+
+ Component.onCompleted: {
+ internalComponent.createObject()
+ gc()
+ }
+}
diff --git a/tests/auto/qml/qqmlcomponent/data/onDestructionLookup.qml b/tests/auto/qml/qqmlcomponent/data/onDestructionLookup.qml
new file mode 100644
index 0000000000..a49a86e1f3
--- /dev/null
+++ b/tests/auto/qml/qqmlcomponent/data/onDestructionLookup.qml
@@ -0,0 +1,25 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ property bool success: false
+
+ Component {
+ id: internalComponent
+
+ Item {
+ id: internalRoot
+
+ property string foo: ''
+
+ Component.onCompleted: { internalRoot.foo = 'bar' }
+ Component.onDestruction: { root.success = (internalRoot.foo == 'bar') }
+ }
+ }
+
+ Component.onCompleted: {
+ internalComponent.createObject()
+ gc()
+ }
+}
diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
index b534d0c788..bf76ed962b 100644
--- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
+++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp
@@ -112,6 +112,8 @@ private slots:
void async();
void asyncHierarchy();
void componentUrlCanonicalization();
+ void onDestructionLookup();
+ void onDestructionCount();
private:
QQmlEngine engine;
@@ -366,6 +368,47 @@ void tst_qqmlcomponent::componentUrlCanonicalization()
}
}
+void tst_qqmlcomponent::onDestructionLookup()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("onDestructionLookup.qml"));
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object != 0);
+ QVERIFY(object->property("success").toBool());
+}
+
+void tst_qqmlcomponent::onDestructionCount()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("onDestructionCount.qml"));
+
+ QLatin1String warning("Component.onDestruction");
+
+ {
+ // Warning should be emitted during create()
+ QTest::ignoreMessage(QtWarningMsg, warning.data());
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object != 0);
+ }
+
+ // Warning should not be emitted any further
+ QCOMPARE(engine.outputWarningsToStandardError(), true);
+
+ warnings.clear();
+ QtMsgHandler old = qInstallMsgHandler(msgHandler);
+
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+
+ qInstallMsgHandler(old);
+
+ engine.setOutputWarningsToStandardError(false);
+ QCOMPARE(engine.outputWarningsToStandardError(), false);
+
+ QCOMPARE(warnings.count(), 0);
+}
+
QTEST_MAIN(tst_qqmlcomponent)
#include "tst_qqmlcomponent.moc"
diff --git a/tests/auto/qml/qqmlecmascript/data/OnDestructionComponent.qml b/tests/auto/qml/qqmlecmascript/data/OnDestructionComponent.qml
new file mode 100644
index 0000000000..dcf46725cd
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/OnDestructionComponent.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+import Qt.test 1.0 as ModApi
+
+Item {
+ id: sec
+
+ property int a: 10
+ Component.onDestruction: ModApi.setSpecificProperty(sec, "a", 20);
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/onDestruction.qml b/tests/auto/qml/qqmlecmascript/data/onDestruction.qml
new file mode 100644
index 0000000000..b2963a1d41
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/onDestruction.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ property Component comp: OnDestructionComponent {
+ property int b: 50
+ onParentChanged: b += a
+ }
+
+ property Item compInstance
+
+ Component.onCompleted: {
+ compInstance = comp.createObject(root);
+ compInstance.destroy();
+ }
+}
+
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h
index f39d5d913d..a289d97fd2 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.h
+++ b/tests/auto/qml/qqmlecmascript/testtypes.h
@@ -1021,6 +1021,8 @@ public:
Q_INVOKABLE void setTrackedObjectProperty(const QString &propName) const { m_trackedObject->setProperty(qPrintable(propName), QVariant(5)); }
Q_INVOKABLE QVariant trackedObjectProperty(const QString &propName) const { return m_trackedObject->property(qPrintable(propName)); }
+ Q_INVOKABLE void setSpecificProperty(QObject *obj, const QString & propName, const QVariant & v) const { obj->setProperty(qPrintable(propName), v); }
+
int qobjectTestProperty() const { return m_testProperty; }
void setQObjectTestProperty(int tp) { m_testProperty = tp; emit qobjectTestPropertyChanged(tp); }
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 4e7f1a255c..11819c5cf0 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -257,6 +257,8 @@ private slots:
void tryStatement();
void replaceBinding();
void deleteRootObjectInCreation();
+ void onDestruction();
+
private:
static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
QQmlEngine engine;
@@ -6632,6 +6634,30 @@ void tst_qqmlecmascript::deleteRootObjectInCreation()
delete obj;
}
+void tst_qqmlecmascript::onDestruction()
+{
+ {
+ // Delete object manually to invoke the associated handlers,
+ // prior to engine destruction.
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj != 0);
+ delete obj;
+ }
+
+ {
+ // In this case, the teardown of the engine causes deletion
+ // of contexts and child items. This triggers the
+ // onDestruction handler of a (previously .destroy()ed)
+ // component instance. This shouldn't crash.
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("onDestruction.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj != 0);
+ }
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"