diff options
author | Simon Hausmann <simon.hausmann@qt.io> | 2018-03-09 16:40:38 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@qt.io> | 2018-03-20 09:44:11 +0000 |
commit | 64e90f393146dadb382d154e7d67a9109ab2492a (patch) | |
tree | c1079ff6af2c9fca9357a9313fffe5590e30dfce /src/qml/qml | |
parent | 22b13921f8067f8a93164875a4ad59bed85b0400 (diff) |
Fix QML data structure version checking for ahead-of-time generated files
We must also do version checking for QML and JS files that were compiled
ahead of time and are embedded in resources. If the lookup for the
original source code fails, then we must generate an appropriate error
message.
As an upside we get better error reporting when trying to load an empty
file and Qt.include() now reports the error message in the statusText
field.
The error reporting for imported scripts was not changed as importing an
empty script is (oddly) allowed.
Task-number: QTBUG-66986
Change-Id: Ie0ef81af371a51ecf8c66ae7954d43f5cc6c12de
Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
Diffstat (limited to 'src/qml/qml')
-rw-r--r-- | src/qml/qml/qqmlmetatype.cpp | 35 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatype_p.h | 12 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader.cpp | 38 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader_p.h | 5 |
4 files changed, 80 insertions, 10 deletions
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 7754f0fddc..8fda7f6f77 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -51,6 +51,7 @@ #include <QtCore/qbitarray.h> #include <QtCore/qreadwritelock.h> #include <QtCore/private/qmetaobject_p.h> +#include <QtCore/qloggingcategory.h> #include <qmetatype.h> #include <qobjectdefs.h> @@ -63,6 +64,8 @@ #include <ctype.h> #include "qqmlcomponent.h" +Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) + QT_BEGIN_NAMESPACE struct QQmlMetaTypeData @@ -2539,18 +2542,46 @@ QList<QQmlType> QQmlMetaType::qmlSingletonTypes() return retn; } -const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUrl &uri) +const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUrl &uri, CachedUnitLookupError *status) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); for (const auto lookup : qAsConst(data->lookupCachedQmlUnit)) { - if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) + if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) { + QString error; + if (!unit->qmlData->verifyHeader(QDateTime(), &error)) { + qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error; + if (status) + *status = CachedUnitLookupError::VersionMismatch; + return nullptr; + } + if (status) + *status = CachedUnitLookupError::NoError; return unit->qmlData; + } } + + if (status) + *status = CachedUnitLookupError::NoUnitFound; + return nullptr; } +void QQmlMetaType::prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) +{ + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + data->lookupCachedQmlUnit.prepend(handler); +} + +void QQmlMetaType::removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) +{ + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + data->lookupCachedQmlUnit.removeAll(handler); +} + /*! Returns the pretty QML type name (e.g. 'Item' instead of 'QtQuickItem') for the given object. */ diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 07bef526ba..cd7afc8a01 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -133,7 +133,17 @@ public: static QList<QQmlPrivate::AutoParentFunction> parentFunctions(); - static const QV4::CompiledData::Unit *findCachedCompilationUnit(const QUrl &uri); + enum class CachedUnitLookupError { + NoError, + NoUnitFound, + VersionMismatch + }; + + static const QV4::CompiledData::Unit *findCachedCompilationUnit(const QUrl &uri, CachedUnitLookupError *status); + + // used by tst_qqmlcachegen.cpp + static void prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler); + static void removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler); static bool namespaceContainsRegistrations(const QString &, int majorVersion); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 2b778b0b63..5572fdad44 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1259,6 +1259,7 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) QML_MEMORY_SCOPE_URL(blob->url()); QQmlDataBlob::SourceCodeData d; d.inlineSourceCode = QString::fromUtf8(data); + d.hasInlineSourceCode = true; setData(blob, d); } @@ -1670,9 +1671,11 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode) typeData = new QQmlTypeData(url, this); // TODO: if (compiledData == 0), is it safe to omit this insertion? m_typeCache.insert(url, typeData); - if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(typeData->url())) { + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(typeData->url(), &error)) { QQmlTypeLoader::loadWithCachedUnit(typeData, cachedUnit, mode); } else { + typeData->setCachedUnitStatus(error); QQmlTypeLoader::load(typeData, mode); } } else if ((mode == PreferSynchronous || mode == Synchronous) && QQmlFile::isSynchronous(url)) { @@ -1727,9 +1730,11 @@ QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &url) scriptBlob = new QQmlScriptBlob(url, this); m_scriptCache.insert(url, scriptBlob); - if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(scriptBlob->url())) { + QQmlMetaType::CachedUnitLookupError error; + if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(scriptBlob->url(), &error)) { QQmlTypeLoader::loadWithCachedUnit(scriptBlob, cachedUnit); } else { + scriptBlob->setCachedUnitStatus(error); QQmlTypeLoader::load(scriptBlob); } } @@ -2428,8 +2433,13 @@ void QQmlTypeData::dataReceived(const SourceCodeData &data) if (isError()) return; - if (!m_backupSourceCode.exists()) { - setError(QQmlTypeLoader::tr("No such file or directory")); + if (!m_backupSourceCode.exists() || m_backupSourceCode.isEmpty()) { + if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) + setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); + else if (!m_backupSourceCode.exists()) + setError(QQmlTypeLoader::tr("No such file or directory")); + else + setError(QQmlTypeLoader::tr("File is empty")); return; } @@ -2981,6 +2991,13 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data) } } + if (!data.exists()) { + if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) + setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); + else + setError(QQmlTypeLoader::tr("No such file or directory")); + return; + } QmlIR::Document irUnit(isDebugging()); @@ -3178,7 +3195,7 @@ void QQmlQmldirData::initializeFromCachedUnit(const QV4::CompiledData::Unit *) QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const { error->clear(); - if (!inlineSourceCode.isEmpty()) + if (hasInlineSourceCode) return inlineSourceCode; QFile f(fileInfo.absoluteFilePath()); @@ -3205,7 +3222,7 @@ QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const { - if (!inlineSourceCode.isEmpty()) + if (hasInlineSourceCode) return QDateTime(); QDateTime timeStamp = fileInfo.lastModified(); @@ -3220,11 +3237,18 @@ QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const bool QQmlDataBlob::SourceCodeData::exists() const { - if (!inlineSourceCode.isEmpty()) + if (hasInlineSourceCode) return true; return fileInfo.exists(); } +bool QQmlDataBlob::SourceCodeData::isEmpty() const +{ + if (hasInlineSourceCode) + return inlineSourceCode.isEmpty(); + return fileInfo.size() == 0; +} + QT_END_NAMESPACE #include "qqmltypeloader.moc" diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 4665d342c1..5988632547 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -140,11 +140,13 @@ public: QString readAll(QString *error) const; QDateTime sourceTimeStamp() const; bool exists() const; + bool isEmpty() const; private: friend class QQmlDataBlob; friend class QQmlTypeLoader; QString inlineSourceCode; QFileInfo fileInfo; + bool hasInlineSourceCode = false; }; protected: @@ -271,6 +273,8 @@ public: const QQmlImports &imports() const { return m_importCache; } + void setCachedUnitStatus(QQmlMetaType::CachedUnitLookupError status) { m_cachedUnitStatus = status; } + protected: bool addImport(const QV4::CompiledData::Import *import, QList<QQmlError> *errors); @@ -293,6 +297,7 @@ public: QQmlImports m_importCache; QHash<const QV4::CompiledData::Import*, int> m_unresolvedImports; QList<QQmlQmldirData *> m_qmldirs; + QQmlMetaType::CachedUnitLookupError m_cachedUnitStatus = QQmlMetaType::CachedUnitLookupError::NoError; }; QQmlTypeLoader(QQmlEngine *); |