diff options
-rw-r--r-- | src/qml/compiler/qv4compileddata.cpp | 47 | ||||
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 3 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_moth.cpp | 7 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_moth_p.h | 2 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_p.h | 1 | ||||
-rw-r--r-- | src/qml/jit/qv4assembler.cpp | 19 | ||||
-rw-r--r-- | src/qml/jit/qv4assembler_p.h | 1 | ||||
-rw-r--r-- | src/qml/jit/qv4isel_masm.cpp | 6 | ||||
-rw-r--r-- | src/qml/jit/qv4isel_masm_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader.cpp | 126 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader_p.h | 2 |
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); |