Skip to content

Commit

Permalink
Fix qml cache invalidation when changing dependent C++ registered QML…
Browse files Browse the repository at this point in the history
… singletons

When a qml file uses a qml singleton, we need to reliably detect when
the singleton changes and re-generate the cache of the qml file using
it. This is a scenario covered and fixed by commit
5b94de0, with the exception that
currently QML singletons registered via qmlRegisterSingleton were not
added to the list of dependent singletons for a qml file. We can fix
this by extending findCompositeSingletons() to also cover the singletons
that do not originate from a qmldir file.

[ChangeLog][Qt][Qml] Fixed bug where sometimes changes to a qml
singleton would not propagate to the users or cause crashes.

Task-number: QTBUG-62243
Change-Id: I16c3d9ba65fd82e898a29b946c341907751135a9
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
  • Loading branch information
tronical authored and laknoll committed Sep 12, 2017
1 parent 78d574a commit 9835871
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/qml/qml/qqmlimport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,17 @@ void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports::
resultList.append(ref);
}
}

if (QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion)) {
module->walkCompositeSingletons([&resultList, &set](const QQmlType &singleton) {
QQmlImports::CompositeSingletonReference ref;
ref.typeName = singleton.elementName();
ref.prefix = set.prefix;
ref.majorVersion = singleton.majorVersion();
ref.minorVersion = singleton.minorVersion();
resultList.append(ref);
});
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions src/qml/qml/qqmlmetatype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,18 @@ QQmlType QQmlTypeModule::type(const QV4::String *name, int minor) const
return QQmlType();
}

void QQmlTypeModule::walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const
{
QMutexLocker lock(metaTypeDataLock());
for (auto typeCandidates = d->typeHash.begin(), end = d->typeHash.end();
typeCandidates != end; ++typeCandidates) {
for (auto type: typeCandidates.value()) {
if (type->regType == QQmlType::CompositeSingletonType)
callback(QQmlType(type));
}
}
}

QQmlTypeModuleVersion::QQmlTypeModuleVersion()
: m_module(0), m_minor(0)
{
Expand Down
2 changes: 2 additions & 0 deletions src/qml/qml/qqmlmetatype_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ class QQmlTypeModule
QQmlType type(const QHashedStringRef &, int) const;
QQmlType type(const QV4::String *, int) const;

void walkCompositeSingletons(const std::function<void(const QQmlType &)> &callback) const;

QQmlTypeModulePrivate *priv() { return d; }
private:
//Used by register functions and creates the QQmlTypeModule for them
Expand Down
61 changes: 61 additions & 0 deletions tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ private slots:
void cacheResources();
void stableOrderOfDependentCompositeTypes();
void singletonDependency();
void cppRegisteredSingletonDependency();
};

// A wrapper around QQmlComponent to ensure the temporary reference counts
Expand Down Expand Up @@ -790,6 +791,66 @@ void tst_qmldiskcache::singletonDependency()
}
}

void tst_qmldiskcache::cppRegisteredSingletonDependency()
{
qmlClearTypeRegistrations();
QScopedPointer<QQmlEngine> engine(new QQmlEngine);

QTemporaryDir tempDir;
QVERIFY(tempDir.isValid());

const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) {
QFile f(tempDir.path() + '/' + fileName);
const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate);
Q_ASSERT(ok);
f.write(contents);
return f.fileName();
};

writeTempFile("MySingleton.qml", "import QtQml 2.0\npragma Singleton\nQtObject { property int value: 42 }");

qmlRegisterSingletonType(QUrl::fromLocalFile(tempDir.path() + QLatin1String("/MySingleton.qml")), "CppRegisteredSingletonDependency", 1, 0, "Singly");

const QString testFilePath = writeTempFile("main.qml", "import QtQml 2.0\nimport CppRegisteredSingletonDependency 1.0\nQtObject {\n"
" function getValue() { return Singly.value; }\n"
"}");

{
CleanlyLoadingComponent component(engine.data(), QUrl::fromLocalFile(testFilePath));
QScopedPointer<QObject> obj(component.create());
QVERIFY(!obj.isNull());
QVariant value;
QVERIFY(QMetaObject::invokeMethod(obj.data(), "getValue", Q_RETURN_ARG(QVariant, value)));
QCOMPARE(value.toInt(), 42);
}

const QString testFileCachePath = testFilePath + QLatin1Char('c');
QVERIFY(QFile::exists(testFileCachePath));
QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified();

engine.reset(new QQmlEngine);
waitForFileSystem();

writeTempFile("MySingleton.qml", "import QtQml 2.0\npragma Singleton\nQtObject { property int value: 100 }");
waitForFileSystem();

{
CleanlyLoadingComponent component(engine.data(), QUrl::fromLocalFile(testFilePath));
QScopedPointer<QObject> obj(component.create());
QVERIFY(!obj.isNull());

{
QVERIFY(QFile::exists(testFileCachePath));
QDateTime newCacheTimeStamp = QFileInfo(testFileCachePath).lastModified();
QVERIFY2(newCacheTimeStamp > initialCacheTimeStamp, qPrintable(newCacheTimeStamp.toString()));
}

QVariant value;
QVERIFY(QMetaObject::invokeMethod(obj.data(), "getValue", Q_RETURN_ARG(QVariant, value)));
QCOMPARE(value.toInt(), 100);
}
}

QTEST_MAIN(tst_qmldiskcache)

#include "tst_qmldiskcache.moc"

0 comments on commit 9835871

Please sign in to comment.