QML: Load qmldir-imports also for the implicit import

Since we load plugins and C++ based types, too, we should just load the
dependent imports as well. Otherwise this is rather confusing.

Fixes: QTBUG-101752
Change-Id: I5bd8afa22e5f16141ef8b47c1a40d3ba8698106c
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Ulf Hermann 2022-03-17 10:13:16 +01:00
parent 5a57bd47fb
commit aff516954d
9 changed files with 140 additions and 54 deletions

View File

@ -248,7 +248,7 @@ public:
QTypeRevision addFileImport(
const QString &uri, const QString &prefix, QTypeRevision version, uint flags,
QQmlImportDatabase *database, QList<QQmlError> *errors);
QQmlImportDatabase *database, QString *localQmldir, QList<QQmlError> *errors);
QTypeRevision updateQmldirContent(const QString &uri, const QString &prefix,
const QString &qmldirIdentifier, const QString& qmldirUrl,
@ -1395,7 +1395,7 @@ QTypeRevision QQmlImportsPrivate::addLibraryImport(
QTypeRevision QQmlImportsPrivate::addFileImport(
const QString& uri, const QString &prefix, QTypeRevision version, uint flags,
QQmlImportDatabase *database, QList<QQmlError> *errors)
QQmlImportDatabase *database, QString *localQmldir, QList<QQmlError> *errors)
{
if (uri.startsWith(Slash) || uri.startsWith(Colon)) {
QQmlError error;
@ -1446,8 +1446,11 @@ QTypeRevision QQmlImportsPrivate::addFileImport(
if (importUri.endsWith(Slash))
importUri.chop(1);
if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty())
if (!typeLoader->absoluteFilePath(localFileOrQrc).isEmpty()) {
qmldirIdentifier = localFileOrQrc;
if (localQmldir)
*localQmldir = qmldirIdentifier;
}
} else if (nameSpace->prefix.isEmpty() && !(flags & QQmlImports::ImportIncomplete)) {
@ -1566,7 +1569,8 @@ QTypeRevision QQmlImportsPrivate::updateQmldirContent(const QString &uri, const
Additionally, this will add the import with lowest instead of highest precedence.
*/
QTypeRevision QQmlImports::addImplicitImport(QQmlImportDatabase *importDb, QList<QQmlError> *errors)
QTypeRevision QQmlImports::addImplicitImport(
QQmlImportDatabase *importDb, QString *localQmldir, QList<QQmlError> *errors)
{
Q_ASSERT(errors);
@ -1574,7 +1578,7 @@ QTypeRevision QQmlImports::addImplicitImport(QQmlImportDatabase *importDb, QList
uint flags = ImportImplicit | (!isLocal(baseUrl()) ? ImportIncomplete : 0);
return d->addFileImport(QLatin1String("."), QString(), QTypeRevision(), flags,
importDb, errors);
importDb, localQmldir, errors);
}
/*!
@ -1624,7 +1628,7 @@ QTypeRevision QQmlImports::addFileImport(
<< "addFileImport:" << qPrintable(baseUrl().toString())
<< uri << version << "as" << prefix;
return d->addFileImport(uri, prefix, version, flags, importDb, errors);
return d->addFileImport(uri, prefix, version, flags, importDb, nullptr, errors);
}
QTypeRevision QQmlImports::addLibraryImport(

View File

@ -162,7 +162,7 @@ public:
= QQmlType::AnyRegistrationType) const;
QTypeRevision addImplicitImport(
QQmlImportDatabase *importDb, QList<QQmlError> *errors);
QQmlImportDatabase *importDb, QString *localQmldir, QList<QQmlError> *errors);
bool addInlineComponentImport(QQmlImportInstance *const importInstance, const QString &name, const QUrl importUrl, QQmlType containingType);

View File

@ -46,6 +46,7 @@
#include <private/qqmlscriptblob_p.h>
#include <private/qqmlscriptdata_p.h>
#include <private/qqmltypecompiler_p.h>
#include <private/qqmltypeloaderqmldircontent_p.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qcryptographichash.h>
@ -187,7 +188,8 @@ bool QQmlTypeData::tryLoadFromDiskCache()
&& !import->version.hasMajorVersion()
&& !import->version.hasMinorVersion()) {
QList<QQmlError> errors;
auto pendingImport = std::make_shared<PendingImport>(this, import);
auto pendingImport = std::make_shared<PendingImport>(
this, import, QQmlImports::ImportImplicit);
if (!fetchQmldir(qmldirUrl, pendingImport, 1, &errors)) {
setError(errors);
return false;
@ -556,7 +558,21 @@ bool QQmlTypeData::loadImplicitImport()
// This will also trigger the loading of the qmldir and the import of any native
// types from available plugins.
QList<QQmlError> implicitImportErrors;
m_importCache.addImplicitImport(importDatabase, &implicitImportErrors);
QString localQmldir;
m_importCache.addImplicitImport(importDatabase, &localQmldir, &implicitImportErrors);
// When loading with QQmlImports::ImportImplicit, the imports are _appended_ to the namespace
// in the order they are loaded. Therefore, the addImplicitImport above gets the highest
// precedence. This is in contrast to normal priority imports. Those are _prepended_ in the
// order they are loaded.
if (!localQmldir.isEmpty()) {
const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(localQmldir);
const QList<QQmlDirParser::Import> moduleImports
= QQmlMetaType::moduleImports(qmldir.typeNamespace(), QTypeRevision())
+ qmldir.imports();
loadDependentImports(moduleImports, QString(), QTypeRevision(),
QQmlImports::ImportImplicit, &implicitImportErrors);
}
if (!implicitImportErrors.isEmpty()) {
setError(implicitImportErrors);

View File

@ -481,13 +481,15 @@ 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, uint 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)
@ -535,7 +537,7 @@ 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 = 0;
@ -563,11 +565,11 @@ bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &da
bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, uint flags,
QList<QQmlError> *errors)
{
return addImport(std::make_shared<PendingImport>(this, import), flags, errors);
return addImport(std::make_shared<PendingImport>(this, import, flags), errors);
}
bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr import, uint flags,
QList<QQmlError> *errors)
bool QQmlTypeLoader::Blob::addImport(
QQmlTypeLoader::Blob::PendingImportPtr import, QList<QQmlError> *errors)
{
Q_ASSERT(errors);
@ -592,7 +594,7 @@ bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr impo
// This is a local library import
const QTypeRevision actualVersion = m_importCache.addLibraryImport(
importDatabase, import->uri, import->qualifier,
import->version, qmldirFilePath, qmldirUrl, flags, errors);
import->version, qmldirFilePath, qmldirUrl, import->flags, errors);
if (!actualVersion.isValid())
return false;
@ -600,7 +602,12 @@ bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr impo
if (actualVersion.hasMajorVersion())
import->version = actualVersion;
if (!loadImportDependencies(import, qmldirFilePath, errors))
if (!loadImportDependencies(
import, qmldirFilePath,
(import->flags & QQmlImports::ImportImplicit)
? QQmlImports::ImportImplicit
: QQmlImports::ImportLowPrecedence,
errors))
return false;
const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirFilePath);
@ -652,7 +659,7 @@ bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr impo
if (!m_importCache.addLibraryImport(
importDatabase, import->uri, import->qualifier, import->version,
QString(), QString(), flags, errors).isValid()) {
QString(), QString(), import->flags, errors).isValid()) {
return false;
}
} else {
@ -672,7 +679,8 @@ bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr impo
// 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);
QString(), QString(), import->flags | QQmlImports::ImportIncomplete,
errors);
if (!version.isValid())
return false;
@ -704,7 +712,7 @@ bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr impo
} else {
Q_ASSERT(import->type == QV4::CompiledData::Import::ImportFile);
bool incomplete = false;
uint flags = 0;
QUrl importUrl(import->uri);
QString path = importUrl.path();
@ -713,11 +721,11 @@ bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr impo
QUrl qmldirUrl = finalUrl().resolved(importUrl);
if (!QQmlImports::isLocal(qmldirUrl)) {
// This is a remote file; the import is currently incomplete
incomplete = true;
flags = QQmlImports::ImportIncomplete;
}
const QTypeRevision version = m_importCache.addFileImport(
importDatabase, import->uri, import->qualifier, import->version, incomplete,
importDatabase, import->uri, import->qualifier, import->version, flags,
errors);
if (!version.isValid())
return false;
@ -726,7 +734,7 @@ bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr impo
if (version.hasMajorVersion())
import->version = version;
if (incomplete) {
if (flags & QQmlImports::ImportIncomplete) {
if (!fetchQmldir(qmldirUrl, import, 1, errors))
return false;
}
@ -753,31 +761,60 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob)
}
}
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, uint flags, QList<QQmlError> *errors)
{
for (const auto &import : imports) {
if (import.flags & QQmlDirParser::Import::Optional)
continue;
auto dependencyImport = std::make_shared<PendingImport>();
dependencyImport->uri = import.module;
dependencyImport->qualifier = qualifier;
dependencyImport->version = (import.flags & QQmlDirParser::Import::Auto)
? version : import.version;
dependencyImport->flags = flags;
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 dependent import \"%1\" version %2.%3")
.arg(dependencyImport->uri)
.arg(dependencyImport->version.majorVersion())
.arg(dependencyImport->version.minorVersion()));
errors->append(error);
return false;
}
}
return true;
}
bool QQmlTypeLoader::Blob::loadImportDependencies(
PendingImportPtr currentImport, const QString &qmldirUri, uint 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)
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)) {
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;
}
if (!loadDependentImports(
implicitImports, currentImport->qualifier, currentImport->version,
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;

View File

@ -93,26 +93,27 @@ public:
struct PendingImport
{
QV4::CompiledData::Import::ImportType type = QV4::CompiledData::Import::ImportType::ImportLibrary;
QString uri;
QString qualifier;
QTypeRevision version;
QV4::CompiledData::Import::ImportType type
= QV4::CompiledData::Import::ImportType::ImportLibrary;
QV4::CompiledData::Location location;
uint flags = 0;
int priority = 0;
QTypeRevision version;
PendingImport() = default;
PendingImport(Blob *blob, const QV4::CompiledData::Import *import);
PendingImport(Blob *blob, const QV4::CompiledData::Import *import, uint flags);
};
using PendingImportPtr = std::shared_ptr<PendingImport>;
protected:
bool addImport(const QV4::CompiledData::Import *import, uint flags,
QList<QQmlError> *errors);
bool addImport(PendingImportPtr import, uint flags, QList<QQmlError> *errors);
bool addImport(PendingImportPtr import, QList<QQmlError> *errors);
bool fetchQmldir(const QUrl &url, PendingImportPtr import, int priority, QList<QQmlError> *errors);
bool updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, PendingImportPtr import, QList<QQmlError> *errors);
@ -124,9 +125,14 @@ public:
void dependencyComplete(QQmlDataBlob *) override;
bool loadImportDependencies(PendingImportPtr currentImport, const QString &qmldirUri, QList<QQmlError> *errors);
bool loadImportDependencies(
PendingImportPtr currentImport, const QString &qmldirUri, uint flags,
QList<QQmlError> *errors);
protected:
bool loadDependentImports(
const QList<QQmlDirParser::Import> &imports, const QString &qualifier,
QTypeRevision version, uint flags, QList<QQmlError> *errors);
virtual QString stringAt(int) const { return QString(); }
bool isDebugging() const;

View File

@ -0,0 +1,4 @@
import QtQml
Rectangle { // from the implicit import itself, taking precedence over its dependencies
Item {} // from a dependency of the implicit import
}

View File

@ -0,0 +1,4 @@
import QtQml
Item {
objectName: "notARectangle"
}

View File

@ -0,0 +1,4 @@
module Something
import QtQuick
A 1.0 A.qml
Rectangle 1.0 B.qml

View File

@ -62,6 +62,7 @@ private slots:
void preferResourcePath();
void invalidFileImport_data();
void invalidFileImport();
void implicitWithDependencies();
};
void tst_QQmlImport::cleanup()
@ -137,6 +138,16 @@ void tst_QQmlImport::invalidFileImport()
"but not absolute paths or resource paths.").arg(import)));
}
void tst_QQmlImport::implicitWithDependencies()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("implicitWithDependencies/A.qml"));
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
QScopedPointer<QObject> o(component.create());
QVERIFY(!o.isNull());
QCOMPARE(o->objectName(), QStringLiteral("notARectangle"));
}
void tst_QQmlImport::testDesignerSupported()
{
QQuickView *window = new QQuickView();