aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmltypeloader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmltypeloader.cpp')
-rw-r--r--src/qml/qml/qqmltypeloader.cpp666
1 files changed, 496 insertions, 170 deletions
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index f2f5cffbf8..09b9dcf452 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -45,14 +45,17 @@
#include <private/qqmlengine_p.h>
#include <private/qqmlglobal_p.h>
#include <private/qqmlthread_p.h>
-#include <private/qqmlcompiler_p.h>
#include <private/qqmlcomponent_p.h>
#include <private/qqmlprofiler_p.h>
#include <private/qqmlmemoryprofiler_p.h>
#include <private/qqmltypecompiler_p.h>
+#include <private/qqmlpropertyvalidator_p.h>
+#include <private/qqmlpropertycachecreator_p.h>
+#include <private/qdeferredcleanup_p.h>
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
+#include <QtCore/qdatetime.h>
#include <QtCore/qdebug.h>
#include <QtCore/qmutex.h>
#include <QtCore/qthread.h>
@@ -60,8 +63,11 @@
#include <QtCore/qdiriterator.h>
#include <QtQml/qqmlcomponent.h>
#include <QtCore/qwaitcondition.h>
+#include <QtCore/qloggingcategory.h>
#include <QtQml/qqmlextensioninterface.h>
+#include <functional>
+
#if defined (Q_OS_UNIX)
#include <sys/types.h>
#include <sys/stat.h>
@@ -98,6 +104,11 @@
#endif
DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS);
+DEFINE_BOOL_CONFIG_OPTION(disableDiskCache, QML_DISABLE_DISK_CACHE);
+DEFINE_BOOL_CONFIG_OPTION(forceDiskCache, QML_FORCE_DISK_CACHE);
+
+Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE)
+Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache")
QT_BEGIN_NAMESPACE
@@ -112,6 +123,7 @@ namespace {
};
}
+#if QT_CONFIG(qml_network)
// This is a lame object that we need to ensure that slots connected to
// QNetworkReply get called in the correct thread (the loader thread).
// As QQmlTypeLoader lives in the main thread, and we can't use
@@ -131,6 +143,7 @@ public slots:
private:
QQmlTypeLoader *l;
};
+#endif // qml_network
class QQmlTypeLoaderThread : public QQmlThread
{
@@ -138,9 +151,10 @@ class QQmlTypeLoaderThread : public QQmlThread
public:
QQmlTypeLoaderThread(QQmlTypeLoader *loader);
+#if QT_CONFIG(qml_network)
QNetworkAccessManager *networkAccessManager() const;
QQmlTypeLoaderNetworkReplyProxy *networkReplyProxy() const;
-
+#endif // qml_network
void load(QQmlDataBlob *b);
void loadAsync(QQmlDataBlob *b);
void loadWithStaticData(QQmlDataBlob *b, const QByteArray &);
@@ -163,11 +177,13 @@ private:
void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri);
QQmlTypeLoader *m_loader;
+#if QT_CONFIG(qml_network)
mutable QNetworkAccessManager *m_networkAccessManager;
mutable QQmlTypeLoaderNetworkReplyProxy *m_networkReplyProxy;
+#endif // qml_network
};
-
+#if QT_CONFIG(qml_network)
QQmlTypeLoaderNetworkReplyProxy::QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l)
: l(l)
{
@@ -196,7 +212,7 @@ void QQmlTypeLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply)
l->networkReplyProgress(reply, replySize, replySize);
l->networkReplyFinished(reply);
}
-
+#endif // qml_network
/*!
\class QQmlDataBlob
@@ -430,6 +446,39 @@ void QQmlDataBlob::setError(const QList<QQmlError> &errors)
tryDone();
}
+void QQmlDataBlob::setError(const QQmlCompileError &error)
+{
+ QQmlError e;
+ e.setColumn(error.location.column);
+ e.setLine(error.location.line);
+ e.setDescription(error.description);
+ e.setUrl(url());
+ setError(e);
+}
+
+void QQmlDataBlob::setError(const QVector<QQmlCompileError> &errors)
+{
+ QList<QQmlError> finalErrors;
+ finalErrors.reserve(errors.count());
+ for (const QQmlCompileError &error: errors) {
+ QQmlError e;
+ e.setColumn(error.location.column);
+ e.setLine(error.location.line);
+ e.setDescription(error.description);
+ e.setUrl(url());
+ finalErrors << e;
+ }
+ setError(finalErrors);
+}
+
+void QQmlDataBlob::setError(const QString &description)
+{
+ QQmlError e;
+ e.setDescription(description);
+ e.setUrl(finalUrl());
+ setError(e);
+}
+
/*!
Wait for \a blob to become complete or to error. If \a blob is already
complete or in error, or this blob is already complete, this has no effect.
@@ -480,6 +529,7 @@ void QQmlDataBlob::done()
{
}
+#if QT_CONFIG(qml_network)
/*!
Invoked if there is a network error while fetching this blob.
@@ -532,6 +582,7 @@ void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError)
setError(error);
}
+#endif // qml_network
/*!
Called if \a blob, which was previously waited for, has an error.
@@ -730,12 +781,16 @@ void QQmlDataBlob::ThreadData::setProgress(quint8 v)
}
QQmlTypeLoaderThread::QQmlTypeLoaderThread(QQmlTypeLoader *loader)
-: m_loader(loader), m_networkAccessManager(0), m_networkReplyProxy(0)
+: m_loader(loader)
+#if QT_CONFIG(qml_network)
+, m_networkAccessManager(0), m_networkReplyProxy(0)
+#endif // qml_network
{
// Do that after initializing all the members.
startup();
}
+#if QT_CONFIG(qml_network)
QNetworkAccessManager *QQmlTypeLoaderThread::networkAccessManager() const
{
Q_ASSERT(isThisThread());
@@ -753,6 +808,7 @@ QQmlTypeLoaderNetworkReplyProxy *QQmlTypeLoaderThread::networkReplyProxy() const
Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first
return m_networkReplyProxy;
}
+#endif // qml_network
void QQmlTypeLoaderThread::load(QQmlDataBlob *b)
{
@@ -810,10 +866,12 @@ void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface,
void QQmlTypeLoaderThread::shutdownThread()
{
+#if QT_CONFIG(qml_network)
delete m_networkAccessManager;
m_networkAccessManager = 0;
delete m_networkReplyProxy;
m_networkReplyProxy = 0;
+#endif // qml_network
}
void QQmlTypeLoaderThread::loadThread(QQmlDataBlob *b)
@@ -899,12 +957,14 @@ void QQmlTypeLoader::invalidate()
m_thread = 0;
}
+#if QT_CONFIG(qml_network)
// Need to delete the network replies after
// the loader thread is shutdown as it could be
// getting new replies while we clear them
for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter)
(*iter)->release();
m_networkReplies.clear();
+#endif // qml_network
}
void QQmlTypeLoader::lock()
@@ -1065,13 +1125,9 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob)
QML_MEMORY_SCOPE_URL(blob->m_url);
if (QQmlFile::isSynchronous(blob->m_url)) {
- QQmlFile file(m_engine, blob->m_url);
-
- if (file.isError()) {
- QQmlError error;
- error.setUrl(blob->m_url);
- error.setDescription(file.error());
- blob->setError(error);
+ const QString fileName = QQmlFile::urlToLocalFileOrQrc(blob->m_url);
+ if (!QQml_isFileCaseCorrect(fileName)) {
+ blob->setError(QLatin1String("File name case mismatch"));
return;
}
@@ -1079,10 +1135,10 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob)
if (blob->m_data.isAsync())
m_thread->callDownloadProgressChanged(blob, 1.);
- setData(blob, &file);
+ setData(blob, fileName);
} else {
-
+#if QT_CONFIG(qml_network)
QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url));
QQmlTypeLoaderNetworkReplyProxy *nrp = m_thread->networkReplyProxy();
blob->addref();
@@ -1099,14 +1155,15 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob)
#ifdef DATABLOB_DEBUG
qWarning("QQmlDataBlob: requested %s", qPrintable(blob->url().toString()));
-#endif
-
+#endif // DATABLOB_DEBUG
+#endif // qml_network
}
}
#define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
#define TYPELOADER_MINIMUM_TRIM_THRESHOLD 64
+#if QT_CONFIG(qml_network)
void QQmlTypeLoader::networkReplyFinished(QNetworkReply *reply)
{
Q_ASSERT(m_thread->isThisThread());
@@ -1162,6 +1219,7 @@ void QQmlTypeLoader::networkReplyProgress(QNetworkReply *reply,
m_thread->callDownloadProgressChanged(blob, blob->m_data.progress());
}
}
+#endif // qml_network
/*!
Return the QQmlEngine associated with this loader
@@ -1197,11 +1255,11 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data)
setData(blob, d);
}
-void QQmlTypeLoader::setData(QQmlDataBlob *blob, QQmlFile *file)
+void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName)
{
QML_MEMORY_SCOPE_URL(blob->url());
QQmlDataBlob::Data d;
- d.d = file;
+ d.d = &fileName;
setData(blob, d);
}
@@ -1252,7 +1310,7 @@ void QQmlTypeLoader::shutdownThread()
}
QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader)
- : QQmlDataBlob(url, type, loader), m_importCache(loader), m_isSingleton(false)
+ : QQmlDataBlob(url, type, loader), m_importCache(loader)
{
}
@@ -1394,13 +1452,10 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
bool incomplete = false;
- QUrl qmldirUrl;
- if (importQualifier.isEmpty()) {
- qmldirUrl = finalUrl().resolved(QUrl(importUri + QLatin1String("/qmldir")));
- if (!QQmlImports::isLocal(qmldirUrl)) {
- // This is a remote file; the import is currently incomplete
- incomplete = true;
- }
+ QUrl qmldirUrl = finalUrl().resolved(QUrl(importUri + QLatin1String("/qmldir")));
+ if (!QQmlImports::isLocal(qmldirUrl)) {
+ // This is a remote file; the import is currently incomplete
+ incomplete = true;
}
if (!m_importCache.addFileImport(importDatabase, importUri, importQualifier, import->majorVersion,
@@ -1416,51 +1471,6 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
return true;
}
-bool QQmlTypeLoader::Blob::addPragma(const QmlIR::Pragma &pragma, QList<QQmlError> *errors)
-{
- Q_ASSERT(errors);
-
- if (pragma.type == QmlIR::Pragma::PragmaSingleton) {
- QUrl myUrl = finalUrl();
-
- QQmlType *ret = QQmlMetaType::qmlType(myUrl, true);
- if (!ret) {
- QQmlError error;
- error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent."));
- error.setUrl(myUrl);
- error.setLine(pragma.location.line);
- error.setColumn(pragma.location.column);
- errors->prepend(error);
- return false;
- }
-
- if (!ret->isCompositeSingleton()) {
- QQmlError error;
- error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(ret->qmlTypeName()));
- error.setUrl(myUrl);
- error.setLine(pragma.location.line);
- error.setColumn(pragma.location.column);
- errors->prepend(error);
- return false;
- }
- // This flag is used for error checking when a qmldir file marks a type as
- // composite singleton, but there is no pragma Singleton defined in QML.
- m_isSingleton = true;
- } else {
- QQmlError error;
- error.setDescription(QLatin1String("Invalid pragma"));
- error.setUrl(finalUrl());
- error.setLine(pragma.location.line);
- error.setColumn(pragma.location.column);
- errors->prepend(error);
- return false;
- }
-
- return true;
-}
-
-
-
void QQmlTypeLoader::Blob::dependencyError(QQmlDataBlob *blob)
{
if (blob->type() == QQmlDataBlob::QmldirFile) {
@@ -1489,6 +1499,11 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob)
}
}
+bool QQmlTypeLoader::Blob::isDebugging() const
+{
+ return QV8Engine::getV4(typeLoader()->engine())->debugger() != 0;
+}
+
bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlError> *errors)
{
bool resolve = true;
@@ -1951,9 +1966,11 @@ void QQmlTypeLoader::trimCache()
for (TypeCache::Iterator iter = m_typeCache.begin(), end = m_typeCache.end(); iter != end; ++iter) {
QQmlTypeData *typeData = iter.value();
- const bool hasError = !typeData->m_compiledData && !typeData->m_errors.isEmpty();
- const bool isNotReferenced = typeData->m_compiledData && typeData->m_compiledData->count() == 1;
- if (typeData->count() == 1 && (hasError || isNotReferenced)) {
+ // 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->count() == 1 && (typeData->isError() || typeData->isComplete())
+ && (!typeData->m_compiledData || typeData->m_compiledData->count() == 1)) {
// There are no live objects of this type
unneededTypes.append(iter);
}
@@ -1963,8 +1980,7 @@ void QQmlTypeLoader::trimCache()
break;
while (!unneededTypes.isEmpty()) {
- TypeCache::Iterator iter = unneededTypes.last();
- unneededTypes.removeLast();
+ TypeCache::Iterator iter = unneededTypes.takeLast();
iter.value()->release();
m_typeCache.erase(iter);
@@ -1994,7 +2010,7 @@ QQmlTypeData::TypeDataCallback::~TypeDataCallback()
QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager)
: QQmlTypeLoader::Blob(url, QmlFile, manager),
- m_typesResolved(false), m_compiledData(0), m_implicitImport(0), m_implicitImportLoaded(false)
+ m_typesResolved(false), m_implicitImportLoaded(false)
{
}
@@ -2007,14 +2023,11 @@ QQmlTypeData::~QQmlTypeData()
if (QQmlTypeData *tdata = m_compositeSingletons.at(ii).typeData)
tdata->release();
}
- for (QHash<int, TypeReference>::ConstIterator it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd();
+ for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd();
it != end; ++it) {
if (QQmlTypeData *tdata = it->typeData)
tdata->release();
}
-
- if (m_compiledData)
- m_compiledData->release();
}
const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const
@@ -2022,19 +2035,9 @@ const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() cons
return m_scripts;
}
-const QSet<QString> &QQmlTypeData::namespaces() const
-{
- return m_namespaces;
-}
-
-const QList<QQmlTypeData::TypeReference> &QQmlTypeData::compositeSingletons() const
-{
- return m_compositeSingletons;
-}
-
-QQmlCompiledData *QQmlTypeData::compiledData() const
+QV4::CompiledData::CompilationUnit *QQmlTypeData::compilationUnit() const
{
- return m_compiledData;
+ return m_compiledData.data();
}
void QQmlTypeData::registerCallback(TypeDataCallback *callback)
@@ -2050,10 +2053,115 @@ void QQmlTypeData::unregisterCallback(TypeDataCallback *callback)
Q_ASSERT(!m_callbacks.contains(callback));
}
+bool QQmlTypeData::tryLoadFromDiskCache()
+{
+ if (disableDiskCache() && !forceDiskCache())
+ return false;
+
+ if (isDebugging())
+ 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(), v4->iselFactory.data(), &error)) {
+ qCDebug(DBG_DISK_CACHE) << "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::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &importCache,
+ const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache)
+{
+ Q_ASSERT(m_compiledData);
+ m_compiledData->importCache = importCache;
+ m_compiledData->resolvedTypes = resolvedTypeCache;
+
+ QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
+
+ {
+ 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()
{
+ QDeferredCleanup cleanup([this]{
+ m_document.reset();
+ m_typeReferences.clear();
+ if (isError())
+ m_compiledData = nullptr;
+ });
+
+ if (isError())
+ return;
+
// Check all script dependencies for errors
- for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
+ for (int ii = 0; ii < m_scripts.count(); ++ii) {
const ScriptReference &script = m_scripts.at(ii);
Q_ASSERT(script.script->isCompleteOrError());
if (script.script->isError()) {
@@ -2065,16 +2173,17 @@ void QQmlTypeData::done()
error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
errors.prepend(error);
setError(errors);
+ return;
}
}
// Check all type dependencies for errors
- for (QHash<int, TypeReference>::ConstIterator it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd();
- !isError() && it != end; ++it) {
+ for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); it != end;
+ ++it) {
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;
@@ -2084,11 +2193,12 @@ void QQmlTypeData::done()
error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
errors.prepend(error);
setError(errors);
+ return;
}
}
// Check all composite singleton type dependencies for errors
- for (int ii = 0; !isError() && ii < m_compositeSingletons.count(); ++ii) {
+ for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) {
const TypeReference &type = m_compositeSingletons.at(ii);
Q_ASSERT(!type.typeData || type.typeData->isCompleteOrError());
if (type.typeData && type.typeData->isError()) {
@@ -2102,27 +2212,102 @@ void QQmlTypeData::done()
error.setDescription(QQmlTypeLoader::tr("Type %1 unavailable").arg(typeName));
errors.prepend(error);
setError(errors);
+ return;
+ }
+ }
+
+ QQmlRefPointer<QQmlTypeNameCache> importCache;
+ QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache;
+ {
+ QQmlCompileError error = buildTypeResolutionCaches(&importCache, &resolvedTypeCache);
+ if (error.isSet()) {
+ setError(error);
+ return;
}
}
- // If the type is CompositeSingleton but there was no pragma Singleton in the
- // QML file, lets report an error.
- QQmlType *type = QQmlMetaType::qmlType(url(), true);
- if (!isError() && type && type->isCompositeSingleton() && !m_isSingleton) {
- QString typeName = type->qmlTypeName();
+ QQmlEngine *const engine = typeLoader()->engine();
- QQmlError error;
- error.setDescription(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName));
- error.setUrl(finalUrl());
- setError(error);
+ // verify if any dependencies changed if we're using a cache
+ if (m_document.isNull() && !m_compiledData->verifyChecksum(engine, resolvedTypeCache)) {
+ qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->url().toString();
+ if (!loadFromSource())
+ return;
+ m_backupSourceCode.clear();
+ m_compiledData = nullptr;
+ }
+
+ if (!m_document.isNull()) {
+ // Compile component
+ compile(importCache, resolvedTypeCache);
+ } else {
+ createTypeAndPropertyCaches(importCache, resolvedTypeCache);
}
- // Compile component
- if (!isError())
- compile();
+ if (isError())
+ return;
- m_document.reset();
- m_implicitImport = 0;
+ {
+ QQmlEnginePrivate *const enginePrivate = QQmlEnginePrivate::get(engine);
+ {
+ // Sanity check property bindings
+ QQmlPropertyValidator validator(enginePrivate, m_importCache, m_compiledData);
+ QVector<QQmlCompileError> errors = validator.validate();
+ if (!errors.isEmpty()) {
+ setError(errors);
+ return;
+ }
+ }
+
+ m_compiledData->finalize(enginePrivate);
+ }
+
+ {
+ QQmlType *type = QQmlMetaType::qmlType(finalUrl(), true);
+ if (m_compiledData && m_compiledData->data->flags & QV4::CompiledData::Unit::IsSingleton) {
+ if (!type) {
+ QQmlError error;
+ error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent."));
+ setError(error);
+ return;
+ } else if (!type->isCompositeSingleton()) {
+ QQmlError error;
+ error.setDescription(QQmlTypeLoader::tr("pragma Singleton used with a non composite singleton type %1").arg(type->qmlTypeName()));
+ setError(error);
+ return;
+ }
+ } else {
+ // If the type is CompositeSingleton but there was no pragma Singleton in the
+ // QML file, lets report an error.
+ if (type && type->isCompositeSingleton()) {
+ QString typeName = type->qmlTypeName();
+ setError(QQmlTypeLoader::tr("qmldir defines type as singleton, but no pragma Singleton found in type %1.").arg(typeName));
+ return;
+ }
+ }
+ }
+
+ {
+ // Collect imported scripts
+ m_compiledData->dependentScripts.reserve(m_scripts.count());
+ for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) {
+ const QQmlTypeData::ScriptReference &script = m_scripts.at(scriptIndex);
+
+ QStringRef qualifier(&script.qualifier);
+ QString enclosingNamespace;
+
+ const int lastDotIndex = qualifier.lastIndexOf(QLatin1Char('.'));
+ if (lastDotIndex != -1) {
+ enclosingNamespace = qualifier.left(lastDotIndex).toString();
+ qualifier = qualifier.mid(lastDotIndex+1);
+ }
+
+ m_compiledData->importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace);
+ QQmlScriptData *scriptData = script.script->scriptData();
+ scriptData->addref();
+ m_compiledData->dependentScripts << scriptData;
+ }
+ }
}
void QQmlTypeData::completed()
@@ -2157,9 +2342,42 @@ bool QQmlTypeData::loadImplicitImport()
void QQmlTypeData::dataReceived(const Data &data)
{
- QString code = QString::fromUtf8(data.data(), data.size());
+ 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.
+
+ if (tryLoadFromDiskCache())
+ return;
+
+ if (isError())
+ return;
+
+ if (!error.isEmpty()) {
+ setError(error);
+ return;
+ }
+
+ if (!loadFromSource())
+ return;
+
+ continueLoadFromIR();
+}
+
+void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit)
+{
+ m_document.reset(new QmlIR::Document(isDebugging()));
+ unit->loadIR(m_document.data(), unit);
+ continueLoadFromIR();
+}
+
+bool QQmlTypeData::loadFromSource()
+{
+ QString code = QString::fromUtf8(m_backupSourceCode);
+ m_document.reset(new QmlIR::Document(isDebugging()));
+ m_document->jsModule.sourceTimeStamp = m_sourceTimeStamp;
QQmlEngine *qmlEngine = typeLoader()->engine();
- m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger != 0));
QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames());
if (!compiler.generateFromQml(code, finalUrlString(), m_document.data())) {
QList<QQmlError> errors;
@@ -2173,23 +2391,14 @@ void QQmlTypeData::dataReceived(const Data &data)
errors << e;
}
setError(errors);
- return;
+ return false;
}
-
- continueLoadFromIR();
-}
-
-void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit)
-{
- QQmlEngine *qmlEngine = typeLoader()->engine();
- m_document.reset(new QmlIR::Document(QV8Engine::getV4(qmlEngine)->debugger != 0));
- unit->loadIR(m_document.data(), unit);
- continueLoadFromIR();
+ return true;
}
void QQmlTypeData::continueLoadFromIR()
{
- m_document->collectTypeReferences();
+ m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd());
m_importCache.setBaseUrl(finalUrl(), finalUrlString());
// For remote URLs, we don't delay the loading of the implicit import
@@ -2202,14 +2411,14 @@ void QQmlTypeData::continueLoadFromIR()
return;
// This qmldir is for the implicit import
QQmlJS::MemoryPool *pool = m_document->jsParserEngine.pool();
- m_implicitImport = pool->New<QV4::CompiledData::Import>();
- m_implicitImport->uriIndex = m_document->registerString(QLatin1String("."));
- m_implicitImport->qualifierIndex = 0; // empty string
- m_implicitImport->majorVersion = -1;
- m_implicitImport->minorVersion = -1;
+ auto implicitImport = pool->New<QV4::CompiledData::Import>();
+ implicitImport->uriIndex = m_document->registerString(QLatin1String("."));
+ implicitImport->qualifierIndex = 0; // empty string
+ implicitImport->majorVersion = -1;
+ implicitImport->minorVersion = -1;
QList<QQmlError> errors;
- if (!fetchQmldir(qmldirUrl, m_implicitImport, 1, &errors)) {
+ if (!fetchQmldir(qmldirUrl, implicitImport, 1, &errors)) {
setError(errors);
return;
}
@@ -2230,14 +2439,6 @@ void QQmlTypeData::continueLoadFromIR()
return;
}
}
-
- foreach (QmlIR::Pragma *pragma, m_document->pragmas) {
- if (!addPragma(*pragma, &errors)) {
- Q_ASSERT(errors.size());
- setError(errors);
- return;
- }
- }
}
void QQmlTypeData::allDependenciesDone()
@@ -2282,20 +2483,34 @@ 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);
}
-void QQmlTypeData::compile()
+void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &importCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache)
{
- Q_ASSERT(m_compiledData == 0);
+ Q_ASSERT(m_compiledData.isNull());
- m_compiledData = new QQmlCompiledData(typeLoader()->engine());
-
- QQmlTypeCompiler compiler(QQmlEnginePrivate::get(typeLoader()->engine()), m_compiledData, this, m_document.data());
- if (!compiler.compile()) {
+ QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
+ QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), importCache, resolvedTypeCache);
+ m_compiledData = compiler.compile();
+ if (!m_compiledData) {
setError(compiler.compilationErrors());
- m_compiledData->release();
- m_compiledData = 0;
+ return;
+ }
+
+ const bool trySaveToDisk = (!disableDiskCache() || forceDiskCache()) && !m_document->jsModule.debugMode;
+ if (trySaveToDisk) {
+ QString errorString;
+ if (m_compiledData->saveToDisk(url(), &errorString)) {
+ QString error;
+ if (!m_compiledData->loadFromDisk(url(), enginePrivate->v4engine()->iselFactory.data(), &error)) {
+ // ignore error, keep using the in-memory compilation unit.
+ }
+ } else {
+ qCDebug(DBG_DISK_CACHE) << "Error saving cached version of" << m_compiledData->url().toString() << "to disk:" << errorString;
+ }
}
}
@@ -2309,13 +2524,13 @@ void QQmlTypeData::resolveTypes()
ScriptReference ref;
//ref.location = ...
- ref.qualifier = script.nameSpace;
if (!script.qualifier.isEmpty())
{
- ref.qualifier.prepend(script.qualifier + QLatin1Char('.'));
-
+ ref.qualifier = script.qualifier + QLatin1Char('.') + script.nameSpace;
// Add a reference to the enclosing namespace
m_namespaces.insert(script.qualifier);
+ } else {
+ ref.qualifier = script.nameSpace;
}
ref.script = blob;
@@ -2325,12 +2540,13 @@ void QQmlTypeData::resolveTypes()
// Lets handle resolved composite singleton types
foreach (const QQmlImports::CompositeSingletonReference &csRef, m_importCache.resolvedCompositeSingletons()) {
TypeReference ref;
- QString typeName = csRef.typeName;
-
+ QString typeName;
if (!csRef.prefix.isEmpty()) {
- typeName.prepend(csRef.prefix + QLatin1Char('.'));
+ typeName = csRef.prefix + QLatin1Char('.') + csRef.typeName;
// Add a reference to the enclosing namespace
m_namespaces.insert(csRef.prefix);
+ } else {
+ typeName = csRef.typeName;
}
int majorVersion = csRef.majorVersion > -1 ? csRef.majorVersion : -1;
@@ -2348,7 +2564,7 @@ void QQmlTypeData::resolveTypes()
}
}
- for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_document->typeReferences.constBegin(), end = m_document->typeReferences.constEnd();
+ for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd();
unresolvedRef != end; ++unresolvedRef) {
TypeReference ref; // resolved reference
@@ -2418,6 +2634,57 @@ void QQmlTypeData::resolveTypes()
}
}
+QQmlCompileError QQmlTypeData::buildTypeResolutionCaches(
+ QQmlRefPointer<QQmlTypeNameCache> *importCache,
+ QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache
+ ) const
+{
+ importCache->adopt(new QQmlTypeNameCache);
+
+ for (const QString &ns: m_namespaces)
+ (*importCache)->add(ns);
+
+ // Add any Composite Singletons that were used to the import cache
+ for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons)
+ (*importCache)->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix);
+
+ m_importCache.populateCache(*importCache);
+
+ QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
+
+ for (auto resolvedType = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd(); resolvedType != end; ++resolvedType) {
+ QScopedPointer<QV4::CompiledData::ResolvedTypeReference> ref(new QV4::CompiledData::ResolvedTypeReference);
+ QQmlType *qmlType = resolvedType->type;
+ if (resolvedType->typeData) {
+ if (resolvedType->needsCreation && qmlType->isCompositeSingleton()) {
+ return QQmlCompileError(resolvedType->location, tr("Composite Singleton Type %1 is not creatable.").arg(qmlType->qmlTypeName()));
+ }
+ ref->compilationUnit = resolvedType->typeData->compilationUnit();
+ } else if (qmlType) {
+ ref->type = qmlType;
+ Q_ASSERT(ref->type);
+
+ if (resolvedType->needsCreation && !ref->type->isCreatable()) {
+ QString reason = ref->type->noCreationReason();
+ if (reason.isEmpty())
+ reason = tr("Element is not creatable.");
+ return QQmlCompileError(resolvedType->location, reason);
+ }
+
+ if (ref->type->containsRevisionedAttributes()) {
+ ref->typePropertyCache = engine->cache(ref->type,
+ resolvedType->minorVersion);
+ }
+ }
+ ref->majorVersion = resolvedType->majorVersion;
+ ref->minorVersion = resolvedType->minorVersion;
+ ref->doDynamicTypeCheck();
+ resolvedTypeCache->insert(resolvedType.key(), ref.take());
+ }
+ QQmlCompileError noError;
+ return noError;
+}
+
bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref)
{
QQmlImportNamespace *typeNamespace = 0;
@@ -2539,10 +2806,6 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent
ctxt->importedScripts = effectiveCtxt->importedScripts;
}
- if (ctxt->imports) {
- ctxt->imports->addref();
- }
-
if (effectiveCtxt) {
ctxt->setParent(effectiveCtxt, true);
} else {
@@ -2630,10 +2893,29 @@ struct EmptyCompilationUnit : public QV4::CompiledData::CompilationUnit
void QQmlScriptBlob::dataReceived(const Data &data)
{
- QString source = QString::fromUtf8(data.data(), data.size());
-
QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine());
- QmlIR::Document irUnit(v4->debugger != 0);
+
+ if (!disableDiskCache() || forceDiskCache()) {
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading();
+ QString error;
+ if (unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) {
+ initializeFromCompilationUnit(unit);
+ return;
+ } else {
+ qCDebug(DBG_DISK_CACHE()) << "Error loading" << url().toString() << "from disk cache:" << error;
+ }
+ }
+
+
+ QmlIR::Document irUnit(isDebugging());
+
+ QString error;
+ QString source = QString::fromUtf8(data.readAll(&error, &irUnit.jsModule.sourceTimeStamp));
+ if (!error.isEmpty()) {
+ setError(error);
+ return;
+ }
+
QmlIR::ScriptDirectivesCollector collector(&irUnit.jsParserEngine, &irUnit.jsGenerator);
QList<QQmlError> errors;
@@ -2650,14 +2932,22 @@ void QQmlScriptBlob::dataReceived(const Data &data)
irUnit.javaScriptCompilationUnit = unit;
irUnit.imports = collector.imports;
if (collector.hasPragmaLibrary)
- irUnit.unitFlags |= QV4::CompiledData::Unit::IsSharedLibrary;
+ irUnit.jsModule.unitFlags |= QV4::CompiledData::Unit::IsSharedLibrary;
QmlIR::QmlUnitGenerator qmlGenerator;
- QV4::CompiledData::Unit *unitData = qmlGenerator.generate(irUnit);
+ QV4::CompiledData::ResolvedTypeReferenceMap emptyDependencies;
+ QV4::CompiledData::Unit *unitData = qmlGenerator.generate(irUnit, m_typeLoader->engine(), emptyDependencies);
Q_ASSERT(!unit->data);
// The js unit owns the data and will free the qml unit.
unit->data = unitData;
+ if (!disableDiskCache() || forceDiskCache()) {
+ QString errorString;
+ if (!unit->saveToDisk(url(), &errorString)) {
+ qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" << unit->url().toString() << "to disk:" << errorString;
+ }
+ }
+
initializeFromCompilationUnit(unit);
}
@@ -2668,8 +2958,11 @@ void QQmlScriptBlob::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *
void QQmlScriptBlob::done()
{
+ if (isError())
+ return;
+
// Check all script dependencies for errors
- for (int ii = 0; !isError() && ii < m_scripts.count(); ++ii) {
+ for (int ii = 0; ii < m_scripts.count(); ++ii) {
const ScriptReference &script = m_scripts.at(ii);
Q_ASSERT(script.script->isCompleteOrError());
if (script.script->isError()) {
@@ -2681,17 +2974,15 @@ void QQmlScriptBlob::done()
error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->url().toString()));
errors.prepend(error);
setError(errors);
+ return;
}
}
- if (isError())
- return;
-
m_scriptData->importCache = new QQmlTypeNameCache();
QSet<QString> ns;
- for (int scriptIndex = 0; !isError() && scriptIndex < m_scripts.count(); ++scriptIndex) {
+ for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) {
const ScriptReference &script = m_scripts.at(scriptIndex);
m_scriptData->scripts.append(script.script);
@@ -2785,7 +3076,12 @@ void QQmlQmldirData::setPriority(int priority)
void QQmlQmldirData::dataReceived(const Data &data)
{
- m_content = QString::fromUtf8(data.data(), data.size());
+ QString error;
+ m_content = QString::fromUtf8(data.readAll(&error));
+ if (!error.isEmpty()) {
+ setError(error);
+ return;
+ }
}
void QQmlQmldirData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *)
@@ -2793,6 +3089,36 @@ void QQmlQmldirData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *
Q_UNIMPLEMENTED();
}
+QByteArray QQmlDataBlob::Data::readAll(QString *error, qint64 *sourceTimeStamp) const
+{
+ Q_ASSERT(!d.isNull());
+ error->clear();
+ if (d.isT1()) {
+ if (sourceTimeStamp)
+ *sourceTimeStamp = 0;
+ return *d.asT1();
+ }
+ QFile f(*d.asT2());
+ if (!f.open(QIODevice::ReadOnly)) {
+ *error = f.errorString();
+ return QByteArray();
+ }
+ 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);
+ if (f.read(data.data(), data.length()) != data.length()) {
+ *error = f.errorString();
+ return QByteArray();
+ }
+ return data;
+}
+
QT_END_NAMESPACE
#include "qqmltypeloader.moc"