Ensure repeated compilation does not fail
authorMatthew Vogt <matthew.vogt@nokia.com>
Mon, 21 May 2012 03:40:22 +0000 (13:40 +1000)
committerQt by Nokia <qt-info@nokia.com>
Mon, 21 May 2012 09:22:43 +0000 (11:22 +0200)
Failure could result from the allocation of an address previously used
for the same class.  The test is repeated for a surprisingly large
count of repetitions to increase the chance of this occurring.

Change-Id: I045902c50021f23adbbd61fccf41b9c38657387a
Reviewed-by: Chris Adams <christopher.adams@nokia.com>
src/qml/qml/qqmlengine.cpp
src/qml/qml/qqmltypeloader.cpp
tests/auto/qml/qqmlengine/data/failedCompilation.1.qml [new file with mode: 0644]
tests/auto/qml/qqmlengine/data/repeatedCompilation.qml [new file with mode: 0644]
tests/auto/qml/qqmlengine/tst_qqmlengine.cpp

index 2ebd13b..5111a4c 100644 (file)
@@ -1962,7 +1962,14 @@ void QQmlEnginePrivate::trimCache()
 
 void QQmlEnginePrivate::typeUnloaded(QQmlTypeData *typeData)
 {
-    unregisterCompositeType(typeData->compiledData()->root);
+    if (typeData && typeData->compiledData()) {
+        const QMetaObject *mo = typeData->compiledData()->root;
+        if (QQmlPropertyCache *pc = propertyCache.value(mo)) {
+            propertyCache.remove(mo);
+            pc->release();
+        }
+        unregisterCompositeType(mo);
+    }
 }
 
 bool QQmlEnginePrivate::isTypeLoaded(const QUrl &url) const
index d550a19..228f2f9 100644 (file)
@@ -1244,6 +1244,7 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode)
 
     if (!typeData) {
         typeData = new QQmlTypeData(url, None, this);
+        // TODO: if (compiledData == 0), is it safe to omit this insertion?
         m_typeCache.insert(url, typeData);
         QQmlDataLoader::load(typeData, mode);
     }
@@ -1606,11 +1607,11 @@ void QQmlTypeLoader::trimCache(void (*callback)(void *, QQmlTypeData *), void *a
             TypeCache::Iterator iter = unneededTypes.last();
             unneededTypes.removeLast();
 
+            QQmlTypeData *typeData = iter.value();
             if (callback)
-                (*callback)(arg, iter.value());
-
+                (*callback)(arg, typeData);
             m_typeCache.erase(iter);
-            iter.value()->release();
+            typeData->release();
         }
     }
 
diff --git a/tests/auto/qml/qqmlengine/data/failedCompilation.1.qml b/tests/auto/qml/qqmlengine/data/failedCompilation.1.qml
new file mode 100644 (file)
index 0000000..0bd3cd0
--- /dev/null
@@ -0,0 +1,4 @@
+// No imports - intentional
+
+Item {
+}
diff --git a/tests/auto/qml/qqmlengine/data/repeatedCompilation.qml b/tests/auto/qml/qqmlengine/data/repeatedCompilation.qml
new file mode 100644 (file)
index 0000000..797381e
--- /dev/null
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+
+VMEExtendVMEComponent {
+    property bool success: false
+
+    Component.onCompleted: {
+        success = (foo == 'bar') && (bar == 'baz')
+    }
+}
index 727635e..85c32d8 100644 (file)
@@ -69,6 +69,9 @@ private slots:
     void clearComponentCache();
     void trimComponentCache();
     void trimComponentCache_data();
+    void repeatedCompilation();
+    void failedCompilation();
+    void failedCompilation_data();
     void outputWarningsToStandardError();
     void objectOwnership();
     void multipleEngines();
@@ -372,6 +375,46 @@ void tst_qqmlengine::trimComponentCache_data()
     QTest::newRow("ScriptComponent") << "testScriptComponent.qml";
 }
 
+void tst_qqmlengine::repeatedCompilation()
+{
+    QQmlEngine engine;
+
+    for (int i = 0; i < 100; ++i) {
+        engine.collectGarbage();
+        engine.trimComponentCache();
+
+        QQmlComponent component(&engine, testFileUrl("repeatedCompilation.qml"));
+        QVERIFY(component.isReady());
+        QScopedPointer<QObject> object(component.create());
+        QVERIFY(object != 0);
+        QCOMPARE(object->property("success").toBool(), true);
+    }
+}
+
+void tst_qqmlengine::failedCompilation()
+{
+    QFETCH(QString, file);
+
+    QQmlEngine engine;
+
+    QQmlComponent component(&engine, testFileUrl(file));
+    QVERIFY(!component.isReady());
+    QScopedPointer<QObject> object(component.create());
+    QVERIFY(object == 0);
+
+    engine.collectGarbage();
+    engine.trimComponentCache();
+    engine.clearComponentCache();
+}
+
+void tst_qqmlengine::failedCompilation_data()
+{
+    QTest::addColumn<QString>("file");
+
+    QTest::newRow("Invalid URL") << "failedCompilation.does.not.exist.qml";
+    QTest::newRow("Invalid content") << "failedCompilation.1.qml";
+}
+
 static QStringList warnings;
 static void msgHandler(QtMsgType, const char *warning)
 {