aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmltypeloader.cpp
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2018-03-15 17:08:21 +0100
committerSimon Hausmann <simon.hausmann@qt.io>2018-03-19 08:39:20 +0000
commita3ad52526f79c1528f170c8affe5af00b68ca61d (patch)
treed85b5b9610f3a0d892607664186cb63d9a4b6e3b /src/qml/qml/qqmltypeloader.cpp
parentd5c7229339a916b2f1f004ec4ea9de89995c003d (diff)
Fix crash when calling QQmlEngine::clearComponentCache()
We must protect various resources in the type loader with our existing lock. The QQmlTypeLoaderQmldirContent is now value based, so that we can release the lock on the shared cache early. Copying it involves adjusting the refcount of the QHash and QString instances in the QQmlDirParser. The safety of this was verified with a TSAN build and the example supplied in the task. It crashed reliably with TASN errors first and with this patch it runs without errors. Task-number: QTBUG-41465 Change-Id: I616843c4b8bdfd65d1277d4faa8cb884d8e77df8 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/qml/qml/qqmltypeloader.cpp')
-rw-r--r--src/qml/qml/qqmltypeloader.cpp55
1 files changed, 30 insertions, 25 deletions
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 5b954605e0..2b778b0b63 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -1370,8 +1370,8 @@ bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::Compile
if (!importQualifier.isEmpty()) {
// Does this library contain any qualified scripts?
QUrl libraryUrl(qmldirUrl);
- const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirIdentifier);
- const auto qmldirScripts = qmldir->scripts();
+ const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirIdentifier);
+ const auto qmldirScripts = qmldir.scripts();
for (const QQmlDirParser::Script &script : qmldirScripts) {
QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName));
QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
@@ -1418,8 +1418,8 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
if (!importQualifier.isEmpty()) {
// Does this library contain any qualified scripts?
QUrl libraryUrl(qmldirUrl);
- const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirFilePath);
- const auto qmldirScripts = qmldir->scripts();
+ const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirFilePath);
+ const auto qmldirScripts = qmldir.scripts();
for (const QQmlDirParser::Script &script : qmldirScripts) {
QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName));
QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
@@ -1584,6 +1584,7 @@ QString QQmlTypeLoaderQmldirContent::typeNamespace() const
void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content)
{
+ m_hasContent = true;
m_location = location;
m_parser.parse(content);
}
@@ -1808,6 +1809,7 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path)
int lastSlash = path.lastIndexOf(QLatin1Char('/'));
QString dirPath(path.left(lastSlash));
+ LockHolder<QQmlTypeLoader> holder(this);
if (!m_importDirCache.contains(dirPath)) {
bool exists = QDir(dirPath).exists();
QCache<QString, bool> *entry = exists ? new QCache<QString, bool> : nullptr;
@@ -1871,6 +1873,7 @@ bool QQmlTypeLoader::directoryExists(const QString &path)
--length;
QString dirPath(path.left(length));
+ LockHolder<QQmlTypeLoader> holder(this);
if (!m_importDirCache.contains(dirPath)) {
bool exists = QDir(dirPath).exists();
QCache<QString, bool> *files = exists ? new QCache<QString, bool> : nullptr;
@@ -1889,8 +1892,10 @@ Return a QQmlTypeLoaderQmldirContent for absoluteFilePath. The QQmlTypeLoaderQm
It can also be a remote path for a remote directory import, but it will have been cached by now in this case.
*/
-const QQmlTypeLoaderQmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePathIn)
+const QQmlTypeLoaderQmldirContent QQmlTypeLoader::qmldirContent(const QString &filePathIn)
{
+ LockHolder<QQmlTypeLoader> holder(this);
+
QString filePath;
// Try to guess if filePathIn is already a URL. This is necessarily fragile, because
@@ -1904,39 +1909,39 @@ const QQmlTypeLoaderQmldirContent *QQmlTypeLoader::qmldirContent(const QString &
filePath = filePathIn;
} else {
filePath = QQmlFile::urlToLocalFileOrQrc(url);
- if (filePath.isEmpty()) // Can't load the remote here, but should be cached
- return *(m_importQmlDirCache.value(filePathIn));
+ if (filePath.isEmpty()) { // Can't load the remote here, but should be cached
+ if (auto entry = m_importQmlDirCache.value(filePathIn))
+ return **entry;
+ else
+ return QQmlTypeLoaderQmldirContent();
+ }
}
- QQmlTypeLoaderQmldirContent *qmldir;
QQmlTypeLoaderQmldirContent **val = m_importQmlDirCache.value(filePath);
- if (!val) {
- qmldir = new QQmlTypeLoaderQmldirContent;
+ if (val)
+ return **val;
+ QQmlTypeLoaderQmldirContent *qmldir = new QQmlTypeLoaderQmldirContent;
#define ERROR(description) { QQmlError e; e.setDescription(description); qmldir->setError(e); }
#define NOT_READABLE_ERROR QString(QLatin1String("module \"$$URI$$\" definition \"%1\" not readable"))
#define CASE_MISMATCH_ERROR QString(QLatin1String("cannot load module \"$$URI$$\": File name case mismatch for \"%1\""))
- QFile file(filePath);
- if (!QQml_isFileCaseCorrect(filePath)) {
- ERROR(CASE_MISMATCH_ERROR.arg(filePath));
- } else if (file.open(QFile::ReadOnly)) {
- QByteArray data = file.readAll();
- qmldir->setContent(filePath, QString::fromUtf8(data));
- } else {
- ERROR(NOT_READABLE_ERROR.arg(filePath));
- }
+ QFile file(filePath);
+ if (!QQml_isFileCaseCorrect(filePath)) {
+ ERROR(CASE_MISMATCH_ERROR.arg(filePath));
+ } else if (file.open(QFile::ReadOnly)) {
+ QByteArray data = file.readAll();
+ qmldir->setContent(filePath, QString::fromUtf8(data));
+ } else {
+ ERROR(NOT_READABLE_ERROR.arg(filePath));
+ }
#undef ERROR
#undef NOT_READABLE_ERROR
#undef CASE_MISMATCH_ERROR
- m_importQmlDirCache.insert(filePath, qmldir);
- } else {
- qmldir = *val;
- }
-
- return qmldir;
+ m_importQmlDirCache.insert(filePath, qmldir);
+ return *qmldir;
}
void QQmlTypeLoader::setQmldirContent(const QString &url, const QString &content)