aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2016-06-06 16:19:47 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2016-07-19 14:50:02 +0000
commite0e50532d29d02c8bcbab01dbfb72377102eaf8f (patch)
tree93e320ee4db05727dffd3de51dd4a77019afe480 /src
parentfa06494d888727d229cd47efbe7265d61c7f7c25 (diff)
Added basic loading of compilation units from disk
This remains hidden behind the QML_DISK_CACHE flag until all backends support it and the verification code is in place. Change-Id: Ic77c64e20a2dc4c43473c47640e09f8070237e85 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/qml/compiler/qv4compileddata.cpp47
-rw-r--r--src/qml/compiler/qv4compileddata_p.h3
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp7
-rw-r--r--src/qml/compiler/qv4isel_moth_p.h2
-rw-r--r--src/qml/compiler/qv4isel_p.h1
-rw-r--r--src/qml/jit/qv4assembler.cpp19
-rw-r--r--src/qml/jit/qv4assembler_p.h1
-rw-r--r--src/qml/jit/qv4isel_masm.cpp6
-rw-r--r--src/qml/jit/qv4isel_masm_p.h1
-rw-r--r--src/qml/qml/qqmltypeloader.cpp126
-rw-r--r--src/qml/qml/qqmltypeloader_p.h2
11 files changed, 209 insertions, 6 deletions
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 17eee03a0a..6a30faba19 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -51,6 +51,9 @@
#include <private/qqmlengine_p.h>
#include <QQmlPropertyMap>
#include <QSaveFile>
+#include <QFile>
+#include <QFileInfo>
+#include <QScopedValueRollback>
#endif
#include <private/qqmlirbuilder_p.h>
#include <QCoreApplication>
@@ -327,6 +330,44 @@ bool CompilationUnit::saveToDisk(QString *errorString)
return true;
}
+bool CompilationUnit::loadFromDisk(const QUrl &url, QString *errorString)
+{
+ if (!url.isLocalFile()) {
+ *errorString = QStringLiteral("File has to be a local file.");
+ return false;
+ }
+
+ QScopedPointer<QFile> cacheFile(new QFile(url.toLocalFile() + QLatin1Char('c')));
+
+ {
+ QFileInfo sourceCode(url.toLocalFile());
+ if (sourceCode.exists() && sourceCode.lastModified() >= QFileInfo(*cacheFile).lastModified()) {
+ *errorString = QStringLiteral("QML source file is equal or newer than cached file.");
+ return false;
+ }
+ }
+
+ if (!cacheFile->open(QIODevice::ReadOnly)) {
+ *errorString = cacheFile->errorString();
+ return false;
+ }
+
+ uchar *cacheData = cacheFile->map(/*offset*/0, cacheFile->size());
+ if (!cacheData) {
+ *errorString = cacheFile->errorString();
+ return false;
+ }
+
+ QScopedValueRollback<const Unit *> dataPtrChange(data, reinterpret_cast<const Unit *>(cacheData));
+
+ if (!memoryMapCode(errorString))
+ return false;
+
+ dataPtrChange.commit();
+ backingFile.reset(cacheFile.take());
+ return true;
+}
+
void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit)
{
Q_UNUSED(unit);
@@ -339,6 +380,12 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QStrin
*errorString = QStringLiteral("Saving code to disk is not supported in this configuration");
return false;
}
+
+bool CompilationUnit::memoryMapCode(QString *errorString)
+{
+ *errorString = QStringLiteral("Missing code mapping backend");
+ return false;
+}
#endif // V4_BOOTSTRAP
Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index eef36f98e6..0f85460a08 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -845,6 +845,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount
int listMetaTypeId;
bool isRegisteredWithEngine;
+ QScopedPointer<QIODevice> backingFile;
// --- interface for QQmlPropertyCacheCreator
typedef Object CompiledObject;
@@ -877,11 +878,13 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount
void destroy() Q_DECL_OVERRIDE;
bool saveToDisk(QString *errorString);
+ bool loadFromDisk(const QUrl &url, QString *errorString);
protected:
virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0;
virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit);
virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString);
+ virtual bool memoryMapCode(QString *errorString);
#endif // V4_BOOTSTRAP
};
diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp
index 8814ad8a78..5fa3a808ab 100644
--- a/src/qml/compiler/qv4isel_moth.cpp
+++ b/src/qml/compiler/qv4isel_moth.cpp
@@ -1594,3 +1594,10 @@ void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine)
runtimeFunctions[i] = runtimeFunction;
}
}
+
+QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading()
+{
+ QQmlRefPointer<CompiledData::CompilationUnit> result;
+ result.adopt(new Moth::CompilationUnit);
+ return result;
+}
diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h
index 29d117af38..f30b540a2b 100644
--- a/src/qml/compiler/qv4isel_moth_p.h
+++ b/src/qml/compiler/qv4isel_moth_p.h
@@ -207,6 +207,8 @@ public:
{ return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); }
virtual bool jitCompileRegexps() const
{ return false; }
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE;
+
};
template<int InstrT>
diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h
index ecafafcea1..d93c0893ae 100644
--- a/src/qml/compiler/qv4isel_p.h
+++ b/src/qml/compiler/qv4isel_p.h
@@ -107,6 +107,7 @@ public:
virtual ~EvalISelFactory() = 0;
virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0;
virtual bool jitCompileRegexps() const = 0;
+ virtual QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading() = 0;
};
namespace IR {
diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp
index d700449e9e..08e4f0a8c0 100644
--- a/src/qml/jit/qv4assembler.cpp
+++ b/src/qml/jit/qv4assembler.cpp
@@ -126,6 +126,25 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit
return true;
}
+bool CompilationUnit::memoryMapCode(QString *errorString)
+{
+ Q_UNUSED(errorString);
+ Q_ASSERT(codeRefs.isEmpty());
+ codeRefs.reserve(data->functionTableSize);
+
+ const char *basePtr = reinterpret_cast<const char *>(data);
+
+ for (uint i = 0; i < data->functionTableSize; ++i) {
+ const CompiledData::Function *compiledFunction = data->functionAt(i);
+ void *codePtr = const_cast<void *>(reinterpret_cast<const void *>(basePtr + compiledFunction->codeOffset));
+ JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr));
+ JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize);
+ codeRefs.append(codeRef);
+ }
+
+ return true;
+}
+
const Assembler::VoidType Assembler::Void;
Assembler::Assembler(InstructionSelection *isel, IR::Function* function, QV4::ExecutableAllocator *executableAllocator)
diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h
index 9373821082..748afbfba4 100644
--- a/src/qml/jit/qv4assembler_p.h
+++ b/src/qml/jit/qv4assembler_p.h
@@ -84,6 +84,7 @@ struct CompilationUnit : public QV4::CompiledData::CompilationUnit
void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE;
void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE;
bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE;
+ bool memoryMapCode(QString *errorString);
// Coderef + execution engine
diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp
index a45d74bb4c..bde2c59526 100644
--- a/src/qml/jit/qv4isel_masm.cpp
+++ b/src/qml/jit/qv4isel_masm.cpp
@@ -1961,5 +1961,11 @@ void InstructionSelection::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *tru
_block, trueBlock, falseBlock);
}
+QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading()
+{
+ QQmlRefPointer<CompiledData::CompilationUnit> result;
+ result.adopt(new JIT::CompilationUnit);
+ return result;
+}
#endif // ENABLE(ASSEMBLER)
diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h
index a92196f5f7..7616ba147f 100644
--- a/src/qml/jit/qv4isel_masm_p.h
+++ b/src/qml/jit/qv4isel_masm_p.h
@@ -290,6 +290,7 @@ public:
{ return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); }
virtual bool jitCompileRegexps() const
{ return true; }
+ QQmlRefPointer<CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE;
};
} // end of namespace JIT
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 190ac29e33..8deb41c505 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -50,6 +50,7 @@
#include <private/qqmlmemoryprofiler_p.h>
#include <private/qqmltypecompiler_p.h>
#include <private/qqmlpropertyvalidator_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
@@ -99,6 +100,7 @@
DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS);
DEFINE_BOOL_CONFIG_OPTION(diskCache, QML_DISK_CACHE);
+DEFINE_BOOL_CONFIG_OPTION(forceDiskCacheRefresh, QML_FORCE_DISK_CACHE_REFRESH);
QT_BEGIN_NAMESPACE
@@ -1953,7 +1955,9 @@ void QQmlTypeLoader::trimCache()
QList<TypeCache::Iterator> unneededTypes;
for (TypeCache::Iterator iter = m_typeCache.begin(), end = m_typeCache.end(); iter != end; ++iter) {
QQmlTypeData *typeData = iter.value();
- if (typeData->m_compiledData && typeData->count() == 1
+ // typeData->m_compiledData may be set early on in the proccess of loading a file, so it's important
+ // to check the general loading status of the typeData before making any other decisions.
+ if (typeData->isComplete() && typeData->m_compiledData && typeData->count() == 1
&& typeData->m_compiledData->count() == 1) {
// There are no live objects of this type
unneededTypes.append(iter);
@@ -2038,6 +2042,106 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
Q_ASSERT(!m_callbacks.contains(callback));
}
+bool QQmlTypeData::tryLoadFromDiskCache()
+{
+ if (!diskCache())
+ return false;
+
+ if (forceDiskCacheRefresh())
+ return false;
+
+ QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine());
+ if (!v4)
+ return false;
+
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading();
+ {
+ QString error;
+ if (!unit->loadFromDisk(url(), &error)) {
+ qDebug() << "Error loading" << url().toString() << "from disk cache:" << error;
+ return false;
+ }
+ }
+
+ m_compiledData = unit;
+
+ for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i)
+ m_typeReferences.collectFromObject(m_compiledData->objectAt(i));
+
+ m_importCache.setBaseUrl(finalUrl(), finalUrlString());
+
+ // For remote URLs, we don't delay the loading of the implicit import
+ // because the loading probably requires an asynchronous fetch of the
+ // qmldir (so we can't load it just in time).
+ if (!finalUrl().scheme().isEmpty()) {
+ QUrl qmldirUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
+ if (!QQmlImports::isLocal(qmldirUrl)) {
+ if (!loadImplicitImport())
+ return false;
+
+ // find the implicit import
+ for (quint32 i = 0; i < m_compiledData->data->nImports; ++i) {
+ const QV4::CompiledData::Import *import = m_compiledData->data->importAt(i);
+ if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".")
+ && import->qualifierIndex == 0
+ && import->majorVersion == -1
+ && import->minorVersion == -1) {
+ QList<QQmlError> errors;
+ if (!fetchQmldir(qmldirUrl, import, 1, &errors)) {
+ setError(errors);
+ return false;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ for (int i = 0, count = m_compiledData->data->nImports; i < count; ++i) {
+ const QV4::CompiledData::Import *import = m_compiledData->data->importAt(i);
+ QList<QQmlError> errors;
+ if (!addImport(import, &errors)) {
+ Q_ASSERT(errors.size());
+ QQmlError error(errors.takeFirst());
+ error.setUrl(m_importCache.baseUrl());
+ error.setLine(import->location.line);
+ error.setColumn(import->location.column);
+ errors.prepend(error); // put it back on the list after filling out information.
+ setError(errors);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void QQmlTypeData::rebuildTypeAndPropertyCaches()
+{
+ Q_ASSERT(m_compiledData);
+
+ QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
+
+ {
+ QQmlCompileError error = buildTypeResolutionCaches(&m_compiledData->importCache, &m_compiledData->resolvedTypes);
+ if (error.isSet()) {
+ setError(error);
+ return;
+ }
+ }
+
+ {
+ QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(&m_compiledData->propertyCaches, engine, m_compiledData, &m_importCache);
+ QQmlCompileError error = propertyCacheCreator.buildMetaObjects();
+ if (error.isSet()) {
+ setError(error);
+ return;
+ }
+ }
+
+ QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> aliasCreator(&m_compiledData->propertyCaches, m_compiledData);
+ aliasCreator.appendAliasPropertiesToMetaObjects();
+}
+
void QQmlTypeData::done()
{
// Check all script dependencies for errors
@@ -2062,7 +2166,7 @@ void QQmlTypeData::done()
const TypeReference &type = *it;
Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
if (type.typeData && type.typeData->isError()) {
- QString typeName = m_document->stringAt(it.key());
+ const QString typeName = stringAt(it.key());
QList<QQmlError> errors = type.typeData->errors();
QQmlError error;
@@ -2093,9 +2197,14 @@ void QQmlTypeData::done()
}
}
- // Compile component
- if (!isError())
- compile();
+ if (!isError()) {
+ if (!m_document.isNull()) {
+ // Compile component
+ compile();
+ } else {
+ rebuildTypeAndPropertyCaches();
+ }
+ }
if (!isError()) {
QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
@@ -2193,6 +2302,9 @@ bool QQmlTypeData::loadImplicitImport()
void QQmlTypeData::dataReceived(const Data &data)
{
+ if (tryLoadFromDiskCache())
+ return;
+
QString error;
QString code = QString::fromUtf8(data.readAll(&error));
if (!error.isEmpty()) {
@@ -2315,6 +2427,8 @@ void QQmlTypeData::downloadProgressChanged(qreal p)
QString QQmlTypeData::stringAt(int index) const
{
+ if (m_compiledData)
+ return m_compiledData->stringAt(index);
return m_document->jsGenerator.stringTable.stringForIndex(index);
}
@@ -2336,7 +2450,7 @@ void QQmlTypeData::compile()
setError(compiler.compilationErrors());
return;
}
- if (diskCache()) {
+ if (diskCache() || forceDiskCacheRefresh()) {
QString errorString;
if (!m_compiledData->saveToDisk(&errorString)) {
qDebug() << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString;
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index 2030dbf427..3e815878f8 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -441,10 +441,12 @@ protected:
virtual QString stringAt(int index) const;
private:
+ bool tryLoadFromDiskCache();
void continueLoadFromIR();
void resolveTypes();
QQmlCompileError buildTypeResolutionCaches(QQmlRefPointer<QQmlTypeNameCache> *importCache, QV4::CompiledData::CompilationUnit::ResolvedTypeReferenceMap *resolvedTypeCache) const;
void compile();
+ void rebuildTypeAndPropertyCaches();
bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref);
virtual void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace);