aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2021-08-10 12:21:58 +0200
committerUlf Hermann <ulf.hermann@qt.io>2021-08-18 11:32:55 +0200
commit3eece79ddbe110c9021ed9b9715c6fd4f70f3ca6 (patch)
treeddc5ed8be89d3f074e72f14b07655d49c09c1b49
parentc39e4b02621a15edc978dbfa45b6a00681f41121 (diff)
Avoid querying the file system for qmldir files for locked modules
If the user explicitly locks a module using qmlProtectModule, we don't load any additional qmldir files afterwards. In particular, this means you can only do that with modules that don't contain qmldir files or with modules for which the qmldir files have already been loaded in all engines that need them. This is in contrast to the "weak" locking we automatically perform when loading a plugin. When importing the module again after loading a plugin we do want to re-evaluate the qmldir directives. If the module is imported from a different engine than before, we also have to search for the qmldir file again. Amends commit 914e0300792856ddac9b99b20a8d88dd6f087fa6. [ChangeLog][QtQml] The pre-5.15 behavior of qmlProtectModule() has mostly been restored: If you explicitly protect a module, the QML engine will never look for any qmldir files or plugins for that module again. This severely limits what you can do with such a module. However, once all affected engines have loaded the module, protecting it can be a useful optimization. In contrast to pre-5.15, the qmldir cache for such modules continues to be re-used even after they are locked. Therefore, in QML engines that have loaded the module before, you can expect any "prefer" or "import" directives and any composite types to still be available after protecting the module. Fixes: QTBUG-85591 Change-Id: Ia4edd860e2ddda5e0c419e1ce9764f10f32ace1f Reviewed-by: Thorbjørn Lindeijer <bjorn@lindeijer.nl> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Andrei Golubev <andrei.golubev@qt.io> Reviewed-by: Fawzi Mohamed <fawzi.mohamed@qt.io> (cherry picked from commit d0dc91158d0b44d9e1b73c3b0dacdd6699741ad7) Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc33
-rw-r--r--src/qml/qml/qqmlimport_p.h13
-rw-r--r--src/qml/qml/qqmlmetatype.cpp25
-rw-r--r--src/qml/qml/qqmlmetatype_p.h4
-rw-r--r--src/qml/qml/qqmltypeloader.cpp7
-rw-r--r--src/qml/qml/qqmltypemodule_p.h23
-rw-r--r--tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp4
7 files changed, 71 insertions, 38 deletions
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc
index 3fc4c86f74..c32eca84ee 100644
--- a/src/qml/doc/src/qmlfunctions.qdoc
+++ b/src/qml/doc/src/qmlfunctions.qdoc
@@ -1243,20 +1243,25 @@
\fn bool qmlProtectModule(const char* uri, int majVersion);
\relates QQmlEngine
- This function protects a module from having types registered into it. This
- can be used to prevent other plugins from injecting types into your module.
- It can also be a performance improvement, as it allows the engine to skip
- checking for the possibility of new types or plugins when this import is
- reached.
-
- The performance benefit is primarily seen when registering application
- specific types from within the application instead of through a plugin.
- Using qmlProtectModule allows the engine to skip checking for a plugin when
- that uri is imported, which can be noticeable with slow file systems.
-
- After this function is called, any attempt to register C++ types into this
- uri, major version combination will lead to a runtime error. Call this after
- you have registered all of your types with the engine.
+ This function protects a module from further modification. This can be used
+ to prevent other plugins from injecting types into your module. It can also
+ be a performance improvement, as it allows the engine to skip checking for
+ the possibility of new types or plugins when this import is reached.
+
+ Once qmlProtectModule has been called, a QML engine will not search for a new
+ \c qmldir file to load the module anymore. It will re-use any \c qmldir files
+ it has loaded before, though. Therefore, types present at this point continue
+ to work. Mind that different QML engines may load different modules. The
+ module protection, however, is global and affects all engines. The overhead
+ of locating \c qmldir files and loading plugins may be noticeable with slow file
+ systems. Therefore, protecting a module once you are sure you won't need to
+ load it anymore can be a good optimization. Mind also that the module lock
+ not only affects plugins but also any other qmldir directives, like \c import
+ or \c prefer, as well as any composite types or scripts declared in a \c qmldir
+ file.
+
+ In addition, after this function is called, any attempt to register C++ types
+ into this uri, major version combination will lead to a runtime error.
Returns true if the module with \a uri as a \l{Identified Modules}
{module identifier} and \a majVersion as a major version number was found
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index 2d27fed648..5ebd7f2a2e 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -217,6 +217,11 @@ class Q_QML_PRIVATE_EXPORT QQmlImportDatabase
public:
enum PathType { Local, Remote, LocalOrRemote };
+ enum LocalQmldirSearchLocation {
+ QmldirFileAndCache,
+ QmldirCacheOnly,
+ };
+
enum LocalQmldirResult {
QmldirFound,
QmldirNotFound,
@@ -240,7 +245,8 @@ public:
template<typename Callback>
LocalQmldirResult locateLocalQmldir(
- const QString &uri, QTypeRevision version, const Callback &callback);
+ const QString &uri, QTypeRevision version, LocalQmldirSearchLocation location,
+ const Callback &callback);
static QTypeRevision lockModule(const QString &uri, const QString &typeNamespace,
QTypeRevision version, QList<QQmlError> *errors);
@@ -273,7 +279,8 @@ private:
template<typename Callback>
QQmlImportDatabase::LocalQmldirResult QQmlImportDatabase::locateLocalQmldir(
- const QString &uri, QTypeRevision version, const Callback &callback)
+ const QString &uri, QTypeRevision version,
+ QQmlImportDatabase::LocalQmldirSearchLocation location, const Callback &callback)
{
// Check cache first
@@ -301,7 +308,7 @@ QQmlImportDatabase::LocalQmldirResult QQmlImportDatabase::locateLocalQmldir(
// Do not try to construct the cache if it already had any entries for the URI.
// Otherwise we might duplicate cache entries.
- if (result != QmldirNotFound)
+ if (location == QmldirCacheOnly || result != QmldirNotFound)
return result;
const bool hasInterceptors = !engine->urlInterceptors().isEmpty();
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index e412263825..8b92a2ff8c 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -431,7 +431,7 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da
if (uri && !typeName.isEmpty()) {
QString nameSpace = QString::fromUtf8(uri);
QQmlTypeModule *qqtm = data->findTypeModule(nameSpace, version);
- if (qqtm && qqtm->isLocked()) {
+ if (qqtm && qqtm->lockLevel() != QQmlTypeModule::LockLevel::Open) {
QString failure(QCoreApplication::translate(
"qmlRegisterType",
"Cannot install %1 '%2' into protected module '%3' version '%4'"));
@@ -645,18 +645,19 @@ void QQmlMetaType::unregisterSequentialContainer(int id)
unregisterType(id);
}
-bool QQmlMetaType::protectModule(const QString &uri, QTypeRevision version, bool protectAllVersions)
+bool QQmlMetaType::protectModule(const QString &uri, QTypeRevision version,
+ bool weakProtectAllVersions)
{
QQmlMetaTypeDataPtr data;
if (version.hasMajorVersion()) {
- if (QQmlTypeModule *module = data->findTypeModule(uri, version)) {
- if (!protectAllVersions) {
- module->lock();
- return true;
+ if (QQmlTypeModule *module = data->findTypeModule(uri, version)) {
+ if (!weakProtectAllVersions) {
+ module->setLockLevel(QQmlTypeModule::LockLevel::Strong);
+ return true;
+ }
+ } else {
+ return false;
}
- } else {
- return false;
- }
}
const auto range = std::equal_range(
@@ -664,7 +665,7 @@ bool QQmlMetaType::protectModule(const QString &uri, QTypeRevision version, bool
std::less<ModuleUri>());
for (auto it = range.first; it != range.second; ++it)
- (*it)->lock();
+ (*it)->setLockLevel(QQmlTypeModule::LockLevel::Weak);
return range.first != range.second;
}
@@ -981,12 +982,12 @@ QTypeRevision QQmlMetaType::latestModuleVersion(const QString &uri)
/*
Returns true if a module \a uri of this version is installed and locked;
*/
-bool QQmlMetaType::isLockedModule(const QString &uri, QTypeRevision version)
+bool QQmlMetaType::isStronglyLockedModule(const QString &uri, QTypeRevision version)
{
QQmlMetaTypeDataPtr data;
if (QQmlTypeModule* qqtm = data->findTypeModule(uri, version))
- return qqtm->isLocked();
+ return qqtm->lockLevel() == QQmlTypeModule::LockLevel::Strong;
return false;
}
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index 7ee16f9dc9..bd7e07373c 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -147,7 +147,7 @@ public:
static void registerModule(const char *uri, QTypeRevision version);
static bool protectModule(const QString &uri, QTypeRevision version,
- bool protectAllVersions = false);
+ bool weakProtectAllVersions = false);
static void registerModuleImport(const QString &uri, QTypeRevision version,
const QQmlDirParser::Import &import);
@@ -197,7 +197,7 @@ public:
static bool isList(QMetaType type);
static QTypeRevision latestModuleVersion(const QString &uri);
- static bool isLockedModule(const QString &uri, QTypeRevision version);
+ static bool isStronglyLockedModule(const QString &uri, QTypeRevision version);
static QTypeRevision matchingModuleVersion(const QString &module, QTypeRevision version);
static QQmlTypeModule *typeModule(const QString &uri, QTypeRevision version);
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 69331a1cd4..59c9f858f6 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -580,9 +580,14 @@ bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr impo
scriptImported(blob, import->location, import->qualifier, QString());
} else if (import->type == QV4::CompiledData::Import::ImportLibrary) {
+ const QQmlImportDatabase::LocalQmldirSearchLocation searchMode =
+ QQmlMetaType::isStronglyLockedModule(import->uri, import->version)
+ ? QQmlImportDatabase::QmldirCacheOnly
+ : QQmlImportDatabase::QmldirFileAndCache;
+
const QQmlImportDatabase::LocalQmldirResult qmldirResult
= importDatabase->locateLocalQmldir(
- import->uri, import->version,
+ import->uri, import->version, searchMode,
[&](const QString &qmldirFilePath, const QString &qmldirUrl) {
// This is a local library import
const QTypeRevision actualVersion = m_importCache.addLibraryImport(
diff --git a/src/qml/qml/qqmltypemodule_p.h b/src/qml/qml/qqmltypemodule_p.h
index 0ba6245cbb..c6dae7ef55 100644
--- a/src/qml/qml/qqmltypemodule_p.h
+++ b/src/qml/qml/qqmltypemodule_p.h
@@ -72,6 +72,12 @@ struct String;
class QQmlTypeModule
{
public:
+ enum class LockLevel {
+ Open = 0,
+ Weak = 1,
+ Strong = 2
+ };
+
QQmlTypeModule() = default;
QQmlTypeModule(const QString &uri, quint8 majorVersion)
: m_module(uri), m_majorVersion(majorVersion)
@@ -80,8 +86,17 @@ public:
void add(QQmlTypePrivate *);
void remove(const QQmlTypePrivate *type);
- bool isLocked() const { return m_locked.loadRelaxed() != 0; }
- void lock() { m_locked.storeRelaxed(1); }
+ LockLevel lockLevel() const { return LockLevel(m_lockLevel.loadRelaxed()); }
+ bool setLockLevel(LockLevel mode)
+ {
+ while (true) {
+ const int currentLock = m_lockLevel.loadAcquire();
+ if (currentLock > int(mode))
+ return false;
+ if (currentLock == int(mode) || m_lockLevel.testAndSetRelease(currentLock, int(mode)))
+ return true;
+ }
+ }
QString module() const
{
@@ -114,8 +129,8 @@ private:
// Can only ever increase
QAtomicInt m_maxMinorVersion = 0;
- // Bool. Can only be set to 1 once.
- QAtomicInt m_locked = 0;
+ // LockLevel. Can only be increased.
+ QAtomicInt m_lockLevel = int(LockLevel::Open);
using TypeHash = QStringHash<QList<QQmlTypePrivate *>>;
TypeHash m_typeHash;
diff --git a/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp
index 4b58348962..8321e02d79 100644
--- a/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp
+++ b/tests/auto/qml/qqmlmoduleplugin/protectedModule/plugin.cpp
@@ -47,9 +47,9 @@ public:
void registerTypes(const char *uri) override
{
- // The module is protected. The plugin can still be loaded, but it cannot register
- // any types.
+ // Because the module is protected, this plugin should never be loaded
Q_UNUSED(uri);
+ Q_UNREACHABLE();
}
};