diff options
Diffstat (limited to 'src/qml/qml/qqmltypeloader.cpp')
-rw-r--r-- | src/qml/qml/qqmltypeloader.cpp | 717 |
1 files changed, 426 insertions, 291 deletions
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index ce1625fb63..a74397bd93 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <private/qqmltypeloader_p.h> @@ -82,6 +46,9 @@ namespace { }; } +Q_TRACE_POINT(qtqml, QQmlCompiling_entry, const QUrl &url) +Q_TRACE_POINT(qtqml, QQmlCompiling_exit) + /*! \class QQmlTypeLoader \brief The QQmlTypeLoader class abstracts loading files and their dependencies over the network. @@ -124,8 +91,6 @@ void QQmlTypeLoader::invalidate() // 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 } @@ -217,9 +182,7 @@ void QQmlTypeLoader::doLoad(const Loader &loader, QQmlDataBlob *blob, Mode mode) } else { Q_ASSERT(mode == Synchronous); while (!blob->isCompleteOrError()) { - unlock(); m_thread->waitForNextMessage(); - lock(); } } } @@ -250,21 +213,21 @@ void QQmlTypeLoader::loadWithCachedUnit(QQmlDataBlob *blob, const QQmlPrivate::C doLoad(CachedLoader(unit), blob, mode); } -void QQmlTypeLoader::loadWithStaticDataThread(QQmlDataBlob *blob, const QByteArray &data) +void QQmlTypeLoader::loadWithStaticDataThread(const QQmlDataBlob::Ptr &blob, const QByteArray &data) { ASSERT_LOADTHREAD(); setData(blob, data); } -void QQmlTypeLoader::loadWithCachedUnitThread(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit) +void QQmlTypeLoader::loadWithCachedUnitThread(const QQmlDataBlob::Ptr &blob, const QQmlPrivate::CachedQmlUnit *unit) { ASSERT_LOADTHREAD(); setCachedUnit(blob, unit); } -void QQmlTypeLoader::loadThread(QQmlDataBlob *blob) +void QQmlTypeLoader::loadThread(const QQmlDataBlob::Ptr &blob) { ASSERT_LOADTHREAD(); @@ -290,7 +253,7 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob) return; } - blob->m_data.setProgress(0xFF); + blob->m_data.setProgress(1.f); if (blob->m_data.isAsync()) m_thread->callDownloadProgressChanged(blob, 1.); @@ -300,7 +263,6 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob) #if QT_CONFIG(qml_network) QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url)); QQmlTypeLoaderNetworkReplyProxy *nrp = m_thread->networkReplyProxy(); - blob->addref(); m_networkReplies.insert(reply, blob); if (reply->isFinished()) { @@ -332,7 +294,7 @@ void QQmlTypeLoader::networkReplyFinished(QNetworkReply *reply) reply->deleteLater(); - QQmlDataBlob *blob = m_networkReplies.take(reply); + QQmlRefPointer<QQmlDataBlob> blob = m_networkReplies.take(reply); Q_ASSERT(blob); @@ -348,7 +310,7 @@ void QQmlTypeLoader::networkReplyFinished(QNetworkReply *reply) QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(url)); QObject *nrp = m_thread->networkReplyProxy(); QObject::connect(reply, SIGNAL(finished()), nrp, SLOT(finished())); - m_networkReplies.insert(reply, blob); + m_networkReplies.insert(reply, std::move(blob)); #ifdef DATABLOB_DEBUG qWarning("QQmlDataBlob: redirected to %s", qPrintable(blob->finalUrlString())); #endif @@ -362,8 +324,6 @@ void QQmlTypeLoader::networkReplyFinished(QNetworkReply *reply) QByteArray data = reply->readAll(); setData(blob, data); } - - blob->release(); } void QQmlTypeLoader::networkReplyProgress(QNetworkReply *reply, @@ -371,12 +331,12 @@ void QQmlTypeLoader::networkReplyProgress(QNetworkReply *reply, { Q_ASSERT(m_thread->isThisThread()); - QQmlDataBlob *blob = m_networkReplies.value(reply); + const QQmlRefPointer<QQmlDataBlob> blob = m_networkReplies.value(reply); Q_ASSERT(blob); if (bytesTotal != 0) { - quint8 progress = 0xFF * (qreal(bytesReceived) / qreal(bytesTotal)); + qreal progress = (qreal(bytesReceived) / qreal(bytesTotal)); blob->m_data.setProgress(progress); if (blob->m_data.isAsync()) m_thread->callDownloadProgressChanged(blob, blob->m_data.progress()); @@ -420,7 +380,7 @@ void QQmlTypeLoader::initializeEngine(QQmlExtensionInterface *iface, const char doInitializeEngine(iface, m_thread, engine(), uri); } -void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) +void QQmlTypeLoader::setData(const QQmlDataBlob::Ptr &blob, const QByteArray &data) { QQmlDataBlob::SourceCodeData d; d.inlineSourceCode = QString::fromUtf8(data); @@ -428,17 +388,17 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) setData(blob, d); } -void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName) +void QQmlTypeLoader::setData(const QQmlDataBlob::Ptr &blob, const QString &fileName) { QQmlDataBlob::SourceCodeData d; d.fileInfo = QFileInfo(fileName); setData(blob, d); } -void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeData &d) +void QQmlTypeLoader::setData(const QQmlDataBlob::Ptr &blob, const QQmlDataBlob::SourceCodeData &d) { Q_TRACE_SCOPE(QQmlCompiling, blob->url()); - QQmlCompilingProfiler prof(profiler(), blob); + QQmlCompilingProfiler prof(profiler(), blob.data()); blob->m_inCallback = true; @@ -455,10 +415,10 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeD blob->tryDone(); } -void QQmlTypeLoader::setCachedUnit(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit) +void QQmlTypeLoader::setCachedUnit(const QQmlDataBlob::Ptr &blob, const QQmlPrivate::CachedQmlUnit *unit) { Q_TRACE_SCOPE(QQmlCompiling, blob->url()); - QQmlCompilingProfiler prof(profiler(), blob); + QQmlCompilingProfiler prof(profiler(), blob.data()); blob->m_inCallback = true; @@ -481,17 +441,21 @@ void QQmlTypeLoader::shutdownThread() m_thread->shutdown(); } -QQmlTypeLoader::Blob::PendingImport::PendingImport(QQmlTypeLoader::Blob *blob, const QV4::CompiledData::Import *import) +QQmlTypeLoader::Blob::PendingImport::PendingImport( + QQmlTypeLoader::Blob *blob, const QV4::CompiledData::Import *import, + QQmlImports::ImportFlags flags) + : uri(blob->stringAt(import->uriIndex)) + , qualifier(blob->stringAt(import->qualifierIndex)) + , type(static_cast<QV4::CompiledData::Import::ImportType>(quint32(import->type))) + , location(import->location) + , flags(flags) + , version(import->version) { - type = static_cast<QV4::CompiledData::Import::ImportType>(quint32(import->type)); - uri = blob->stringAt(import->uriIndex); - qualifier = blob->stringAt(import->qualifierIndex); - version = import->version; - location = import->location; } QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoader *loader) - : QQmlDataBlob(url, type, loader), m_importCache(loader) + : QQmlDataBlob(url, type, loader) + , m_importCache(new QQmlImports(), QQmlRefPointer<QQmlImports>::Adopt) { } @@ -503,8 +467,7 @@ bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, PendingImportPtr import, { QQmlRefPointer<QQmlQmldirData> data = typeLoader()->getQmldir(url); - data->setImport(this, std::move(import)); - data->setPriority(this, priority); + data->setPriority(this, std::move(import), priority); if (data->status() == Error) { // This qmldir must not exist - which is not an error @@ -519,16 +482,55 @@ bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, PendingImportPtr import, return true; } -bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, PendingImportPtr import, QList<QQmlError> *errors) +/*! + * \internal + * Import any qualified scripts of for \a import as listed in \a qmldir. + * Precondition is that \a import is actually qualified. + */ +void QQmlTypeLoader::Blob::importQmldirScripts( + const QQmlTypeLoader::Blob::PendingImportPtr &import, + const QQmlTypeLoaderQmldirContent &qmldir, const QUrl &qmldirUrl) +{ + const auto qmldirScripts = qmldir.scripts(); + for (const QQmlDirParser::Script &script : qmldirScripts) { + const QUrl scriptUrl = qmldirUrl.resolved(QUrl(script.fileName)); + QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl); + addDependency(blob.data()); + scriptImported(blob, import->location, script.nameSpace, import->qualifier); + } +} + +template<typename URL> +void postProcessQmldir( + QQmlTypeLoader::Blob *self, + const QQmlTypeLoader::Blob::PendingImportPtr &import, const QString &qmldirFilePath, + const URL &qmldirUrl) +{ + const QQmlTypeLoaderQmldirContent qmldir = self->typeLoader()->qmldirContent(qmldirFilePath); + if (!import->qualifier.isEmpty()) + self->importQmldirScripts(import, qmldir, QUrl(qmldirUrl)); + + if (qmldir.plugins().isEmpty()) { + // If the qmldir does not register a plugin, we might still have declaratively + // registered types (if we are dealing with an application instead of a library) + // We should use module name given in the qmldir rather than the one given by the + // import since the import may be a directory import. + auto module = QQmlMetaType::typeModule(qmldir.typeNamespace(), import->version); + if (!module) + QQmlMetaType::qmlRegisterModuleTypes(qmldir.typeNamespace()); + // else: If the module already exists, the types must have been already registered + } +} + +bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, const QQmlTypeLoader::Blob::PendingImportPtr &import, QList<QQmlError> *errors) { QString qmldirIdentifier = data->urlString(); QString qmldirUrl = qmldirIdentifier.left(qmldirIdentifier.lastIndexOf(QLatin1Char('/')) + 1); typeLoader()->setQmldirContent(qmldirIdentifier, data->content()); - const QTypeRevision version = m_importCache.updateQmldirContent( - typeLoader()->importDatabase(), import->uri, import->qualifier, qmldirIdentifier, - qmldirUrl, errors); + const QTypeRevision version = m_importCache->updateQmldirContent( + typeLoader(), import->uri, import->qualifier, qmldirIdentifier, qmldirUrl, errors); if (!version.isValid()) return false; @@ -536,233 +538,283 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &da if (version.hasMajorVersion()) import->version = version; - if (!loadImportDependencies(import, qmldirIdentifier, errors)) + if (!loadImportDependencies(import, qmldirIdentifier, import->flags, errors)) return false; - import->priority = data->priority(this); + import->priority = 0; // Release this reference at destruction m_qmldirs << data; - if (!import->qualifier.isEmpty()) { - // Does this library contain any qualified scripts? - QUrl libraryUrl(qmldirUrl); - const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirIdentifier); - const auto qmldirScripts = qmldir.scripts(); - for (const QQmlDirParser::Script &script : qmldirScripts) { - QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); - QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl); - addDependency(blob.data()); - - scriptImported(blob, import->location, script.nameSpace, import->qualifier); - } - } + postProcessQmldir(this, import, qmldirIdentifier, qmldirUrl); + return true; +} +bool QQmlTypeLoader::Blob::addScriptImport(const QQmlTypeLoader::Blob::PendingImportPtr &import) +{ + const QUrl url(import->uri); + QQmlTypeLoader *loader = typeLoader(); + QQmlRefPointer<QQmlScriptBlob> blob = loader->injectedScript(url); + if (!blob) + blob = loader->getScript(finalUrl().resolved(url)); + else + Q_ASSERT(blob->status() == QQmlDataBlob::Status::Complete); + addDependency(blob.data()); + scriptImported(blob, import->location, import->qualifier, QString()); return true; } -bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, uint flags, - QList<QQmlError> *errors) +bool QQmlTypeLoader::Blob::addFileImport(const QQmlTypeLoader::Blob::PendingImportPtr &import, QList<QQmlError> *errors) { - return addImport(std::make_shared<PendingImport>(this, import), flags, errors); + QQmlImports::ImportFlags flags; + + QUrl importUrl(import->uri); + QString path = importUrl.path(); + path.append(QLatin1String(path.endsWith(QLatin1Char('/')) ? "qmldir" : "/qmldir")); + importUrl.setPath(path); + QUrl qmldirUrl = finalUrl().resolved(importUrl); + if (!QQmlImports::isLocal(qmldirUrl)) { + // This is a remote file; the import is currently incomplete + flags = QQmlImports::ImportIncomplete; + } + + const QTypeRevision version = m_importCache->addFileImport( + typeLoader(), import->uri, import->qualifier, import->version, flags, + import->precedence, nullptr, errors); + if (!version.isValid()) + return false; + + // Use more specific version for the qmldir if possible + if (version.hasMajorVersion()) + import->version = version; + + if (flags & QQmlImports::ImportIncomplete) { + if (!fetchQmldir(qmldirUrl, import, 1, errors)) + return false; + } else { + const QString qmldirFilePath = QQmlFile::urlToLocalFileOrQrc(qmldirUrl); + if (!loadImportDependencies(import, qmldirFilePath, import->flags, errors)) + return false; + + postProcessQmldir(this, import, qmldirFilePath, qmldirUrl); + } + + return true; } -bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr import, uint flags, - QList<QQmlError> *errors) +static void addDependencyImportError( + const QQmlTypeLoader::Blob::PendingImportPtr &import, QList<QQmlError> *errors) { - Q_ASSERT(errors); + QQmlError error; + QString reason = errors->front().description(); + if (reason.size() > 512) + reason = reason.first(252) + QLatin1String("... ...") + reason.last(252); + if (import->version.hasMajorVersion()) { + error.setDescription(QQmlImportDatabase::tr( + "module \"%1\" version %2.%3 cannot be imported because:\n%4") + .arg(import->uri).arg(import->version.majorVersion()) + .arg(import->version.hasMinorVersion() + ? QString::number(import->version.minorVersion()) + : QLatin1String("x")) + .arg(reason)); + } else { + error.setDescription(QQmlImportDatabase::tr("module \"%1\" cannot be imported because:\n%2") + .arg(import->uri, reason)); + } + errors->prepend(error); +} +bool QQmlTypeLoader::Blob::addLibraryImport(const QQmlTypeLoader::Blob::PendingImportPtr &import, QList<QQmlError> *errors) +{ QQmlImportDatabase *importDatabase = typeLoader()->importDatabase(); - if (import->type == QV4::CompiledData::Import::ImportScript) { - QUrl scriptUrl = finalUrl().resolved(QUrl(import->uri)); - QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl); - addDependency(blob.data()); + const QQmlImportDatabase::LocalQmldirSearchLocation searchMode = + QQmlMetaType::isStronglyLockedModule(import->uri, import->version) + ? QQmlImportDatabase::QmldirCacheOnly + : QQmlImportDatabase::QmldirFileAndCache; + + const QQmlImportDatabase::LocalQmldirResult qmldirResult + = importDatabase->locateLocalQmldir( + import->uri, import->version, searchMode, + [&](const QString &qmldirFilePath, const QString &qmldirUrl) { + // This is a local library import + const QTypeRevision actualVersion = m_importCache->addLibraryImport( + typeLoader(), import->uri, import->qualifier, import->version, qmldirFilePath, + qmldirUrl, import->flags, import->precedence, errors); + if (!actualVersion.isValid()) + return false; - scriptImported(blob, import->location, import->qualifier, QString()); - } else if (import->type == QV4::CompiledData::Import::ImportLibrary) { - QString qmldirFilePath; - QString qmldirUrl; - - const QQmlImports::LocalQmldirResult qmldirResult = m_importCache.locateLocalQmldir( - importDatabase, import->uri, import->version, - &qmldirFilePath, &qmldirUrl); - if (qmldirResult == QQmlImports::QmldirFound) { - // This is a local library import - const QTypeRevision actualVersion = m_importCache.addLibraryImport( - importDatabase, import->uri, import->qualifier, - import->version, qmldirFilePath, qmldirUrl, flags, errors); - if (!actualVersion.isValid()) - return false; + // Use more specific version for dependencies if possible + if (actualVersion.hasMajorVersion()) + import->version = actualVersion; - // Use more specific version for dependencies if possible - if (actualVersion.hasMajorVersion()) - import->version = actualVersion; + if (!loadImportDependencies(import, qmldirFilePath, import->flags, errors)) { + addDependencyImportError(import, errors); + return false; + } - if (!loadImportDependencies(import, qmldirFilePath, errors)) - return false; + postProcessQmldir(this, import, qmldirFilePath, qmldirUrl); + return true; + }); - const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirFilePath); - if (!import->qualifier.isEmpty()) { - // Does this library contain any qualified scripts? - QUrl libraryUrl(qmldirUrl); - const auto qmldirScripts = qmldir.scripts(); - for (const QQmlDirParser::Script &script : qmldirScripts) { - QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); - QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl); - addDependency(blob.data()); - - scriptImported(blob, import->location, script.nameSpace, import->qualifier); - } - } - if (!qmldir.plugins().count()) { - // If the qmldir does not register a plugin, we might still have declaratively - // registered types (if we are dealing with an application instead of a library) - auto module = QQmlMetaType::typeModule(import->uri, import->version); - // If the module already exists, the types must have been already registered - if (!module) - QQmlMetaType::qmlRegisterModuleTypes(import->uri); - } - } else if ( - // Major version of module already registered: - // We believe that the registration is complete. - QQmlMetaType::typeModule(import->uri, import->version) - - // Otherwise, try to register further module types. - || (qmldirResult != QQmlImports::QmldirInterceptedToRemote - && QQmlMetaType::qmlRegisterModuleTypes(import->uri)) - - // Otherwise, there is no way to register any further types. - // Try with any module of that name. - || QQmlMetaType::latestModuleVersion(import->uri).isValid()) { - - if (!m_importCache.addLibraryImport( - importDatabase, import->uri, import->qualifier, import->version, - QString(), QString(), flags, errors).isValid()) { + switch (qmldirResult) { + case QQmlImportDatabase::QmldirFound: + return true; + case QQmlImportDatabase::QmldirNotFound: { + if (!loadImportDependencies(import, QString(), import->flags, errors)) { + addDependencyImportError(import, errors); + return false; + } + break; + } + case QQmlImportDatabase::QmldirInterceptedToRemote: + break; + case QQmlImportDatabase::QmldirRejected: + return false; + } + + // If there is a qmldir we cannot see, yet, then we have to wait. + // The qmldir might contain import directives. + if (qmldirResult != QQmlImportDatabase::QmldirInterceptedToRemote && ( + // Major version of module already registered: + // We believe that the registration is complete. + QQmlMetaType::typeModule(import->uri, import->version) + + // Otherwise, try to register further module types. + || QQmlMetaType::qmlRegisterModuleTypes(import->uri) + + // Otherwise, there is no way to register any further types. + // Try with any module of that name. + || QQmlMetaType::latestModuleVersion(import->uri).isValid())) { + + if (!m_importCache->addLibraryImport( + typeLoader(), import->uri, import->qualifier, import->version, QString(), + QString(), import->flags, import->precedence, errors).isValid()) { + return false; + } + } else { + // We haven't yet resolved this import + m_unresolvedImports << import; + + const QQmlEngine *engine = typeLoader()->engine(); + const bool hasInterceptors + = !(QQmlEnginePrivate::get(engine)->urlInterceptors.isEmpty()); + + // Query any network import paths for this library. + // Interceptor might redirect local paths. + QStringList remotePathList = importDatabase->importPathList( + hasInterceptors ? QQmlImportDatabase::LocalOrRemote + : QQmlImportDatabase::Remote); + if (!remotePathList.isEmpty()) { + // Add this library and request the possible locations for it + const QTypeRevision version = m_importCache->addLibraryImport( + typeLoader(), import->uri, import->qualifier, import->version, QString(), + QString(), import->flags | QQmlImports::ImportIncomplete, import->precedence, + errors); + + if (!version.isValid()) return false; - } - } else { - // We haven't yet resolved this import - m_unresolvedImports << import; - - const QQmlEngine *engine = typeLoader()->engine(); - const bool hasInterceptors - = !(QQmlEnginePrivate::get(engine)->urlInterceptors.isEmpty()); - - // Query any network import paths for this library. - // Interceptor might redirect local paths. - QStringList remotePathList = importDatabase->importPathList( - hasInterceptors ? QQmlImportDatabase::LocalOrRemote - : QQmlImportDatabase::Remote); - if (!remotePathList.isEmpty()) { - // Add this library and request the possible locations for it - const QTypeRevision version = m_importCache.addLibraryImport( - importDatabase, import->uri, import->qualifier, import->version, - QString(), QString(), flags | QQmlImports::ImportIncomplete, errors); - - if (!version.isValid()) - return false; - // Use more specific version for finding the qmldir if possible - if (version.hasMajorVersion()) - import->version = version; - - // Probe for all possible locations - int priority = 0; - const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths( - import->uri, remotePathList, import->version); - for (const QString &qmldirPath : qmlDirPaths) { - if (hasInterceptors) { - QUrl url = engine->interceptUrl( - QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath), - QQmlAbstractUrlInterceptor::QmldirFile); - if (!QQmlFile::isLocalFile(url) - && !fetchQmldir(url, import, ++priority, errors)) { - return false; - } - } else if (!fetchQmldir(QUrl(qmldirPath), import, ++priority, errors)) { + // Use more specific version for finding the qmldir if possible + if (version.hasMajorVersion()) + import->version = version; + + // Probe for all possible locations + int priority = 0; + const QStringList qmlDirPaths = QQmlImports::completeQmldirPaths( + import->uri, remotePathList, import->version); + for (const QString &qmldirPath : qmlDirPaths) { + if (hasInterceptors) { + QUrl url = engine->interceptUrl( + QQmlImports::urlFromLocalFileOrQrcOrUrl(qmldirPath), + QQmlAbstractUrlInterceptor::QmldirFile); + if (!QQmlFile::isLocalFile(url) + && !fetchQmldir(url, import, ++priority, errors)) { return false; } - + } else if (!fetchQmldir(QUrl(qmldirPath), import, ++priority, errors)) { + return false; } + } } - } else { - Q_ASSERT(import->type == QV4::CompiledData::Import::ImportFile); - - bool incomplete = false; - - QUrl importUrl(import->uri); - QString path = importUrl.path(); - path.append(QLatin1String(path.endsWith(QLatin1Char('/')) ? "qmldir" : "/qmldir")); - importUrl.setPath(path); - QUrl qmldirUrl = finalUrl().resolved(importUrl); - if (!QQmlImports::isLocal(qmldirUrl)) { - // This is a remote file; the import is currently incomplete - incomplete = true; - } + } - const QTypeRevision version = m_importCache.addFileImport( - importDatabase, import->uri, import->qualifier, import->version, incomplete, - errors); - if (!version.isValid()) - return false; + return true; +} - // Use more specific version for the qmldir if possible - if (version.hasMajorVersion()) - import->version = version; +bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, + QQmlImports::ImportFlags flags, QList<QQmlError> *errors) +{ + return addImport(std::make_shared<PendingImport>(this, import, flags), errors); +} - if (incomplete) { - if (!fetchQmldir(qmldirUrl, import, 1, errors)) - return false; - } +bool QQmlTypeLoader::Blob::addImport( + QQmlTypeLoader::Blob::PendingImportPtr import, QList<QQmlError> *errors) +{ + Q_ASSERT(errors); + + switch (import->type) + { + case QV4::CompiledData::Import::ImportLibrary: + return addLibraryImport(import, errors); + case QV4::CompiledData::Import::ImportFile: + return addFileImport(import ,errors); + case QV4::CompiledData::Import::ImportScript: + return addScriptImport(import); + case QV4::CompiledData::Import::ImportInlineComponent: + Q_UNREACHABLE_RETURN(false); // addImport is never called with an inline component import } - return true; + Q_UNREACHABLE_RETURN(false); } void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob) { if (blob->type() == QQmlDataBlob::QmldirFile) { QQmlQmldirData *data = static_cast<QQmlQmldirData *>(blob); - - PendingImportPtr import = data->import(this); - QList<QQmlError> errors; if (!qmldirDataAvailable(data, &errors)) { Q_ASSERT(errors.size()); QQmlError error(errors.takeFirst()); - error.setUrl(m_importCache.baseUrl()); - error.setLine(qmlConvertSourceCoordinate<quint32, int>(import->location.line)); - error.setColumn(qmlConvertSourceCoordinate<quint32, int>(import->location.column)); + error.setUrl(m_importCache->baseUrl()); + const QV4::CompiledData::Location importLocation = data->importLocation(this); + error.setLine(qmlConvertSourceCoordinate<quint32, int>(importLocation.line())); + error.setColumn(qmlConvertSourceCoordinate<quint32, int>(importLocation.column())); errors.prepend(error); // put it back on the list after filling out information. setError(errors); } } } -bool QQmlTypeLoader::Blob::loadImportDependencies(PendingImportPtr currentImport, const QString &qmldirUri, QList<QQmlError> *errors) +bool QQmlTypeLoader::Blob::loadDependentImports( + const QList<QQmlDirParser::Import> &imports, const QString &qualifier, + QTypeRevision version, quint16 precedence, QQmlImports::ImportFlags flags, + QList<QQmlError> *errors) { - const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirUri); - const QList<QQmlDirParser::Import> implicitImports - = QQmlMetaType::moduleImports(currentImport->uri, currentImport->version) - + qmldir.imports(); - for (const auto &implicitImport : implicitImports) { - if (implicitImport.flags & QQmlDirParser::Import::Optional) + for (const auto &import : imports) { + if (import.flags & QQmlDirParser::Import::Optional) continue; auto dependencyImport = std::make_shared<PendingImport>(); - dependencyImport->uri = implicitImport.module; - dependencyImport->qualifier = currentImport->qualifier; - dependencyImport->version = (implicitImport.flags & QQmlDirParser::Import::Auto) - ? currentImport->version : implicitImport.version; - if (!addImport(dependencyImport, QQmlImports::ImportLowPrecedence, errors)) { + dependencyImport->uri = import.module; + dependencyImport->qualifier = qualifier; + dependencyImport->version = (import.flags & QQmlDirParser::Import::Auto) + ? version : import.version; + dependencyImport->flags = flags; + dependencyImport->precedence = precedence; + + qCDebug(lcQmlImport) + << "loading dependent import" << dependencyImport->uri << "version" + << dependencyImport->version << "as" << dependencyImport->qualifier; + + if (!addImport(dependencyImport, errors)) { QQmlError error; error.setDescription( QString::fromLatin1( - "Failed to load dependencies for module \"%1\" version %2.%3") - .arg(currentImport->uri) - .arg(currentImport->version.majorVersion()) - .arg(currentImport->version.minorVersion())); + "Failed to load dependent import \"%1\" version %2.%3") + .arg(dependencyImport->uri) + .arg(dependencyImport->version.majorVersion()) + .arg(dependencyImport->version.minorVersion())); errors->append(error); return false; } @@ -771,40 +823,82 @@ bool QQmlTypeLoader::Blob::loadImportDependencies(PendingImportPtr currentImport return true; } +bool QQmlTypeLoader::Blob::loadImportDependencies( + const QQmlTypeLoader::Blob::PendingImportPtr ¤tImport, const QString &qmldirUri, + QQmlImports::ImportFlags flags, QList<QQmlError> *errors) +{ + QList<QQmlDirParser::Import> implicitImports + = QQmlMetaType::moduleImports(currentImport->uri, currentImport->version); + if (!qmldirUri.isEmpty()) + implicitImports += typeLoader()->qmldirContent(qmldirUri).imports(); + + // Prevent overflow from one category of import into the other. + switch (currentImport->precedence) { + case QQmlImportInstance::Implicit - 1: + case QQmlImportInstance::Lowest: { + QQmlError error; + error.setDescription( + QString::fromLatin1("Too many dependent imports for %1 %2.%3") + .arg(currentImport->uri) + .arg(currentImport->version.majorVersion()) + .arg(currentImport->version.minorVersion())); + errors->append(error); + return false; + } + default: + break; + } + + if (!loadDependentImports( + implicitImports, currentImport->qualifier, currentImport->version, + currentImport->precedence + 1, flags, errors)) { + QQmlError error; + error.setDescription( + QString::fromLatin1( + "Failed to load dependencies for module \"%1\" version %2.%3") + .arg(currentImport->uri) + .arg(currentImport->version.majorVersion()) + .arg(currentImport->version.minorVersion())); + errors->append(error); + return false; + } + + return true; +} + bool QQmlTypeLoader::Blob::isDebugging() const { return typeLoader()->engine()->handle()->debugger() != nullptr; } -bool QQmlTypeLoader::Blob::diskCacheEnabled() const +bool QQmlTypeLoader::Blob::readCacheFile() const { - return typeLoader()->engine()->handle()->diskCacheEnabled(); + return typeLoader()->engine()->handle()->diskCacheOptions() + & QV4::ExecutionEngine::DiskCache::QmlcRead; } -bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors) +bool QQmlTypeLoader::Blob::writeCacheFile() const { - PendingImportPtr import = data->import(this); - data->setImport(this, nullptr); - - int priority = data->priority(this); - data->setPriority(this, 0); - - if (import) { - // Do we need to resolve this import? - const bool resolve = (import->priority == 0) || (import->priority > priority); - - if (resolve) { - // This is the (current) best resolution for this import - if (!updateQmldir(data, import, errors)) { - return false; - } + return typeLoader()->engine()->handle()->diskCacheOptions() + & QV4::ExecutionEngine::DiskCache::QmlcWrite; +} - import->priority = priority; - return true; - } - } +QQmlMetaType::CacheMode QQmlTypeLoader::Blob::aotCacheMode() const +{ + const QV4::ExecutionEngine::DiskCacheOptions options + = typeLoader()->engine()->handle()->diskCacheOptions(); + if (!(options & QV4::ExecutionEngine::DiskCache::Aot)) + return QQmlMetaType::RejectAll; + if (options & QV4::ExecutionEngine::DiskCache::AotByteCode) + return QQmlMetaType::AcceptUntyped; + return QQmlMetaType::RequireFullyTyped; +} - return true; +bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors) +{ + return data->processImports(this, [&](PendingImportPtr import) { + return updateQmldir(data, import, errors); + }); } /*! @@ -870,8 +964,9 @@ QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QUrl &unNormalizedUrl m_typeCache.insert(url, typeData); QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; - if (const QQmlPrivate::CachedQmlUnit *cachedUnit = typeData->diskCacheEnabled() - ? QQmlMetaType::findCachedCompilationUnit(typeData->url(), &error) + const QQmlMetaType::CacheMode cacheMode = typeData->aotCacheMode(); + if (const QQmlPrivate::CachedQmlUnit *cachedUnit = (cacheMode != QQmlMetaType::RejectAll) + ? QQmlMetaType::findCachedCompilationUnit(typeData->url(), cacheMode, &error) : nullptr) { QQmlTypeLoader::loadWithCachedUnit(typeData, cachedUnit, mode); } else { @@ -882,16 +977,16 @@ QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QUrl &unNormalizedUrl // this was started Asynchronous, but we need to force Synchronous // completion now (if at all possible with this type of URL). +#if QT_CONFIG(thread) if (!m_thread->isThisThread()) { // this only works when called directly from the UI thread, but not // when recursively called on the QML thread via resolveTypes() while (!typeData->isCompleteOrError()) { - unlock(); m_thread->waitForNextMessage(); - lock(); } } +#endif } return typeData; @@ -911,6 +1006,26 @@ QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QByteArray &data, con return QQmlRefPointer<QQmlTypeData>(typeData, QQmlRefPointer<QQmlTypeData>::Adopt); } +void QQmlTypeLoader::injectScript(const QUrl &relativeUrl, const QV4::Value &value) +{ + LockHolder<QQmlTypeLoader> holder(this); + + QQmlScriptBlob *blob = new QQmlScriptBlob(relativeUrl, this); + blob->initializeFromNative(value); + blob->m_isDone = true; + blob->m_data.setStatus(QQmlDataBlob::Complete); + m_scriptCache.insert(relativeUrl, blob); +} + +QQmlRefPointer<QQmlScriptBlob> QQmlTypeLoader::injectedScript(const QUrl &relativeUrl) +{ + LockHolder<QQmlTypeLoader> holder(this); + const auto it = m_scriptCache.constFind(relativeUrl); + return (it != m_scriptCache.constEnd() && (*it)->isNative()) + ? *it + : QQmlRefPointer<QQmlScriptBlob>(); +} + /*! Return a QQmlScriptBlob for \a url. The QQmlScriptData may be cached. */ @@ -931,8 +1046,9 @@ QQmlRefPointer<QQmlScriptBlob> QQmlTypeLoader::getScript(const QUrl &unNormalize m_scriptCache.insert(url, scriptBlob); QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; - if (const QQmlPrivate::CachedQmlUnit *cachedUnit = scriptBlob->diskCacheEnabled() - ? QQmlMetaType::findCachedCompilationUnit(scriptBlob->url(), &error) + const QQmlMetaType::CacheMode cacheMode = scriptBlob->aotCacheMode(); + if (const QQmlPrivate::CachedQmlUnit *cachedUnit = (cacheMode != QQmlMetaType::RejectAll) + ? QQmlMetaType::findCachedCompilationUnit(scriptBlob->url(), cacheMode, &error) : nullptr) { QQmlTypeLoader::loadWithCachedUnit(scriptBlob, cachedUnit); } else { @@ -982,19 +1098,19 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) // qrc resource QFileInfo fileInfo(path); return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString(); - } else if (path.count() > 3 && path.at(3) == QLatin1Char(':') && + } else if (path.size() > 3 && path.at(3) == QLatin1Char(':') && path.startsWith(QLatin1String("qrc"), Qt::CaseInsensitive)) { // qrc resource url QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path)); return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString(); } #if defined(Q_OS_ANDROID) - else if (path.count() > 7 && path.at(6) == QLatin1Char(':') && path.at(7) == QLatin1Char('/') && + else if (path.size() > 7 && path.at(6) == QLatin1Char(':') && path.at(7) == QLatin1Char('/') && path.startsWith(QLatin1String("assets"), Qt::CaseInsensitive)) { // assets resource url QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path)); return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString(); - } else if (path.count() > 8 && path.at(7) == QLatin1Char(':') && path.at(8) == QLatin1Char('/') && + } else if (path.size() > 8 && path.at(7) == QLatin1Char(':') && path.at(8) == QLatin1Char('/') && path.startsWith(QLatin1String("content"), Qt::CaseInsensitive)) { // content url QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path)); @@ -1016,7 +1132,7 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) return QString(); QString absoluteFilePath; - QString fileName(path.mid(lastSlash+1, path.length()-lastSlash-1)); + QString fileName(path.mid(lastSlash+1, path.size()-lastSlash-1)); bool *value = fileSet->object(fileName); if (value) { @@ -1029,7 +1145,7 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) absoluteFilePath = path; } - if (absoluteFilePath.length() > 2 && absoluteFilePath.at(0) != QLatin1Char('/') && absoluteFilePath.at(1) != QLatin1Char(':')) + if (absoluteFilePath.size() > 2 && absoluteFilePath.at(0) != QLatin1Char('/') && absoluteFilePath.at(1) != QLatin1Char(':')) absoluteFilePath = QFileInfo(absoluteFilePath).absoluteFilePath(); return absoluteFilePath; @@ -1071,20 +1187,20 @@ bool QQmlTypeLoader::fileExists(const QString &path, const QString &file) return addToCache(QFileInfo(path + file)); } - if (path.count() > 3 && path.at(3) == QLatin1Char(':') + if (path.size() > 3 && path.at(3) == QLatin1Char(':') && path.startsWith(QLatin1String("qrc"), Qt::CaseInsensitive)) { // qrc resource url return addToCache(QFileInfo(QQmlFile::urlToLocalFileOrQrc(path + file))); } #if defined(Q_OS_ANDROID) - if (path.count() > 7 && path.at(6) == QLatin1Char(':') && path.at(7) == QLatin1Char('/') + if (path.size() > 7 && path.at(6) == QLatin1Char(':') && path.at(7) == QLatin1Char('/') && path.startsWith(QLatin1String("assets"), Qt::CaseInsensitive)) { // assets resource url return addToCache(QFileInfo(QQmlFile::urlToLocalFileOrQrc(path + file))); } - if (path.count() > 8 && path.at(7) == QLatin1Char(':') && path.at(8) == QLatin1Char('/') + if (path.size() > 8 && path.at(7) == QLatin1Char(':') && path.at(8) == QLatin1Char('/') && path.startsWith(QLatin1String("content"), Qt::CaseInsensitive)) { // content url return addToCache(QFileInfo(QQmlFile::urlToLocalFileOrQrc(path + file))); @@ -1115,7 +1231,7 @@ bool QQmlTypeLoader::directoryExists(const QString &path) return fileInfo.exists() && fileInfo.isDir(); } - int length = path.length(); + int length = path.size(); if (path.endsWith(QLatin1Char('/'))) --length; QString dirPath(path.left(length)); @@ -1152,7 +1268,7 @@ const QQmlTypeLoaderQmldirContent QQmlTypeLoader::qmldirContent(const QString &f // Yet, this heuristic is the best we can do until we pass more structured information here, // for example a QUrl also for local files. QUrl url(filePathIn); - if (url.scheme().length() < 2) { + if (url.scheme().size() < 2) { filePath = filePathIn; } else { filePath = QQmlFile::urlToLocalFileOrQrc(url); @@ -1212,6 +1328,11 @@ and qmldir information. */ void QQmlTypeLoader::clearCache() { + // Pending messages typically hold references to the blobs they want to be delivered to. + // We don't want them anymore. + if (m_thread) + m_thread->discardMessages(); + for (TypeCache::Iterator iter = m_typeCache.begin(), end = m_typeCache.end(); iter != end; ++iter) (*iter)->release(); for (ScriptCache::Iterator iter = m_scriptCache.begin(), end = m_scriptCache.end(); iter != end; ++iter) @@ -1227,7 +1348,7 @@ void QQmlTypeLoader::clearCache() m_qmldirCache.clear(); m_importDirCache.clear(); m_importQmlDirCache.clear(); - QQmlMetaType::freeUnusedTypesAndCaches(); + m_checksumCache.clear(); } void QQmlTypeLoader::updateTypeCacheTrimThreshold() @@ -1249,15 +1370,29 @@ void QQmlTypeLoader::trimCache() // 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 - iter.value()->release(); - iter = m_typeCache.erase(iter); - deletedOneType = true; - } else { + if (typeData->count() != 1 || (!typeData->isError() && !typeData->isComplete())) { ++iter; + continue; + } + + const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit + = typeData->m_compiledData; + if (compilationUnit) { + if (compilationUnit->count() + > QQmlMetaType::countInternalCompositeTypeSelfReferences( + compilationUnit) + 1) { + ++iter; + continue; + } + + QQmlMetaType::unregisterInternalCompositeType(compilationUnit); + Q_ASSERT(compilationUnit->count() == 1); } + + // There are no live objects of this type + iter.value()->release(); + iter = m_typeCache.erase(iter); + deletedOneType = true; } if (!deletedOneType) |