From 5ce2235f425b3c5047faf709c87c9b5c8e414b8b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 15 Mar 2017 16:52:10 +0100 Subject: Avoid reading (not parsing) .qml files when using the cache By making SourceCodeData copyable we can delay the reading of the source file until we really need to. This also allows persisting the QFileInfo object and therefore having only one stat() call to check if the file exists, what its size is and what the last modification time is. Change-Id: Ic7e4d5f566d870f3b1fa8302227417fa813cb139 Reviewed-by: Lars Knoll --- src/qml/qml/qqmltypeloader.cpp | 78 +++++++++++++++++++++++++----------------- src/qml/qml/qqmltypeloader_p.h | 12 ++++--- 2 files changed, 54 insertions(+), 36 deletions(-) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 193239e680..c5d4945bf1 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1246,8 +1246,7 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) { QML_MEMORY_SCOPE_URL(blob->url()); QQmlDataBlob::SourceCodeData d; - d.inlineSourceCodeOrFileName = QString::fromUtf8(data); - d.sourceCodeAvailable = true; + d.inlineSourceCode = QString::fromUtf8(data); setData(blob, d); } @@ -1255,8 +1254,7 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName) { QML_MEMORY_SCOPE_URL(blob->url()); QQmlDataBlob::SourceCodeData d; - d.inlineSourceCodeOrFileName = fileName; - d.sourceCodeAvailable = false; + d.fileInfo = QFileInfo(fileName); setData(blob, d); } @@ -2237,7 +2235,7 @@ void QQmlTypeData::done() qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->url().toString(); if (!loadFromSource()) return; - m_backupSourceCode.clear(); + m_backupSourceCode = SourceCodeData(); m_compiledData = nullptr; } @@ -2346,11 +2344,7 @@ bool QQmlTypeData::loadImplicitImport() void QQmlTypeData::dataReceived(const SourceCodeData &data) { - QString error; - m_backupSourceCode = data.readAll(&error, &m_sourceTimeStamp); - // if we failed to read the source code, process it _after_ we've tried - // to use the disk cache, in order to support scenarios where the source - // was removed deliberately. + m_backupSourceCode = data; if (tryLoadFromDiskCache()) return; @@ -2358,8 +2352,8 @@ void QQmlTypeData::dataReceived(const SourceCodeData &data) if (isError()) return; - if (!error.isEmpty()) { - setError(error); + if (!m_backupSourceCode.exists()) { + setError(QQmlTypeLoader::tr("No such file or directory")); return; } @@ -2379,10 +2373,18 @@ void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *un bool QQmlTypeData::loadFromSource() { m_document.reset(new QmlIR::Document(isDebugging())); - m_document->jsModule.sourceTimeStamp = m_sourceTimeStamp; + m_document->jsModule.sourceTimeStamp = m_backupSourceCode.sourceTimeStamp(); QQmlEngine *qmlEngine = typeLoader()->engine(); QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames()); - if (!compiler.generateFromQml(m_backupSourceCode, finalUrlString(), m_document.data())) { + + QString sourceError; + const QString source = m_backupSourceCode.readAll(&sourceError); + if (!sourceError.isEmpty()) { + setError(sourceError); + return false; + } + + if (!compiler.generateFromQml(source, finalUrlString(), m_document.data())) { QList errors; errors.reserve(compiler.errors.count()); for (const QQmlJS::DiagnosticMessage &msg : qAsConst(compiler.errors)) { @@ -2891,8 +2893,9 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data) QmlIR::Document irUnit(isDebugging()); + irUnit.jsModule.sourceTimeStamp = data.sourceTimeStamp(); QString error; - QString source = data.readAll(&error, &irUnit.jsModule.sourceTimeStamp); + QString source = data.readAll(&error); if (!error.isEmpty()) { setError(error); return; @@ -3071,28 +3074,19 @@ void QQmlQmldirData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit * Q_UNIMPLEMENTED(); } -QString QQmlDataBlob::SourceCodeData::readAll(QString *error, qint64 *sourceTimeStamp) const +QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const { error->clear(); - if (sourceCodeAvailable) { - if (sourceTimeStamp) - *sourceTimeStamp = 0; - return inlineSourceCodeOrFileName; - } - QFile f(inlineSourceCodeOrFileName); + if (!inlineSourceCode.isEmpty()) + return inlineSourceCode; + + QFile f(fileInfo.absoluteFilePath()); if (!f.open(QIODevice::ReadOnly)) { *error = f.errorString(); return QString(); } - if (sourceTimeStamp) { - QDateTime timeStamp = QFileInfo(f).lastModified(); - // Files from the resource system do not have any time stamps, so fall back to the application - // executable. - if (!timeStamp.isValid()) - timeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); - *sourceTimeStamp = timeStamp.toMSecsSinceEpoch(); - } - QByteArray data(f.size(), Qt::Uninitialized); + + QByteArray data(fileInfo.size(), Qt::Uninitialized); if (f.read(data.data(), data.length()) != data.length()) { *error = f.errorString(); return QString(); @@ -3100,6 +3094,28 @@ QString QQmlDataBlob::SourceCodeData::readAll(QString *error, qint64 *sourceTime return QString::fromUtf8(data); } +qint64 QQmlDataBlob::SourceCodeData::sourceTimeStamp() const +{ + if (!inlineSourceCode.isEmpty()) + return 0; + + QDateTime timeStamp = fileInfo.lastModified(); + if (timeStamp.isValid()) + return timeStamp.toMSecsSinceEpoch(); + + static qint64 appTimeStamp = 0; + if (appTimeStamp == 0) + appTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified().toMSecsSinceEpoch(); + return appTimeStamp; +} + +bool QQmlDataBlob::SourceCodeData::exists() const +{ + if (!inlineSourceCode.isEmpty()) + return true; + return fileInfo.exists(); +} + QT_END_NAMESPACE #include "qqmltypeloader.moc" diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index c1b3548fa6..0bae857874 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -54,6 +54,7 @@ #include #include #include +#include #if QT_CONFIG(qml_network) #include #endif @@ -132,12 +133,14 @@ public: class SourceCodeData { public: - QString readAll(QString *error, qint64 *sourceTimeStamp = 0) const; + QString readAll(QString *error) const; + qint64 sourceTimeStamp() const; + bool exists() const; private: friend class QQmlDataBlob; friend class QQmlTypeLoader; - QString inlineSourceCodeOrFileName; - bool sourceCodeAvailable = false; + QString inlineSourceCode; + QFileInfo fileInfo; }; protected: @@ -460,8 +463,7 @@ private: void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override; - qint64 m_sourceTimeStamp = 0; - QString m_backupSourceCode; // used when cache verification fails. + SourceCodeData m_backupSourceCode; // used when cache verification fails. QScopedPointer m_document; QV4::CompiledData::TypeReferenceMap m_typeReferences; -- cgit v1.2.3