aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmlcompiler
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2020-10-14 12:55:08 +0200
committerUlf Hermann <ulf.hermann@qt.io>2020-10-15 10:44:20 +0200
commitf98c961da6d039621ae40ab6c1a79c4b06efb83f (patch)
tree0eca1d6c2e19e54795da143ecb65279acd972572 /src/qmlcompiler
parented9ffa109eb2f242b59443ad430204a90c8028c4 (diff)
qmllint: Defer resolution of types read from QML files
When importing a directory we most likely don't need all of the files in the directory. Therefore we now parse them only when they are accessed. This speeds up the execution and will allow us to process imports recursively without running into infinite recursion. Change-Id: I0c79313de792249e6bb86144b5014a7787dbdc5b Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qmlcompiler')
-rw-r--r--src/qmlcompiler/CMakeLists.txt1
-rw-r--r--src/qmlcompiler/qdeferredpointer_p.h181
-rw-r--r--src/qmlcompiler/qmlcompiler.pro1
-rw-r--r--src/qmlcompiler/qqmljsimporter.cpp26
-rw-r--r--src/qmlcompiler/qqmljsimporter_p.h2
-rw-r--r--src/qmlcompiler/qqmljsimportvisitor.cpp2
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp25
-rw-r--r--src/qmlcompiler/qqmljsscope_p.h41
8 files changed, 253 insertions, 26 deletions
diff --git a/src/qmlcompiler/CMakeLists.txt b/src/qmlcompiler/CMakeLists.txt
index 0eab79259f..d0e9be9418 100644
--- a/src/qmlcompiler/CMakeLists.txt
+++ b/src/qmlcompiler/CMakeLists.txt
@@ -8,6 +8,7 @@ qt_internal_add_module(QmlCompiler
STATIC
INTERNAL_MODULE
SOURCES
+ qdeferredpointer_p.h
qqmljsimporter.cpp qqmljsimporter_p.h
qqmljsimportvisitor.cpp qqmljsimportvisitor_p.h
qqmljsmetatypes_p.h
diff --git a/src/qmlcompiler/qdeferredpointer_p.h b/src/qmlcompiler/qdeferredpointer_p.h
new file mode 100644
index 0000000000..07875013fa
--- /dev/null
+++ b/src/qmlcompiler/qdeferredpointer_p.h
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDEFERREDPOINTER_P_H
+#define QDEFERREDPOINTER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qsharedpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+template<typename T>
+class QDeferredFactory
+{
+public:
+ T create() const;
+ bool isValid() const;
+ void clear();
+};
+
+template<typename T>
+class QDeferredWeakPointer;
+
+template<typename T>
+class QDeferredSharedPointer
+{
+public:
+ using Factory = QDeferredFactory<std::remove_const_t<T>>;
+
+ QDeferredSharedPointer() = default;
+
+ QDeferredSharedPointer(QSharedPointer<T> data)
+ : m_data(data)
+ {}
+
+ QDeferredSharedPointer(QWeakPointer<T> data)
+ : m_data(data)
+ {}
+
+ QDeferredSharedPointer(QSharedPointer<T> data, QSharedPointer<Factory> factory)
+ : m_data(data), m_factory(factory)
+ {}
+
+ operator QSharedPointer<T>() const
+ {
+ if (m_factory && m_factory->isValid()) {
+ const_cast<std::remove_const_t<T> &>(*m_data) = m_factory->create();
+ m_factory->clear();
+ }
+ return m_data;
+ }
+
+ operator QDeferredSharedPointer<const T>() const { return { m_data, m_factory }; }
+
+ T &operator*() const { return QSharedPointer<T>(*this).operator*(); }
+ T *operator->() const { return QSharedPointer<T>(*this).operator->(); }
+
+ bool isNull() const { return m_data.isNull(); }
+ explicit operator bool() const noexcept { return !isNull(); }
+ bool operator !() const noexcept { return isNull(); }
+
+ T *data() const { return QSharedPointer<T>(*this).data(); }
+
+ friend qsizetype qHash(const QDeferredSharedPointer &ptr, qsizetype seed = 0)
+ {
+ return qHashMulti(seed, ptr.m_factory, ptr.m_data);
+ }
+
+ friend bool operator==(const QDeferredSharedPointer &a, const QDeferredSharedPointer &b)
+ {
+ return a.m_factory == b.m_factory && a.m_data == b.m_data;
+ }
+
+private:
+ friend class QDeferredWeakPointer<T>;
+
+ QSharedPointer<T> m_data;
+ QSharedPointer<Factory> m_factory;
+};
+
+template<typename T>
+class QDeferredWeakPointer
+{
+public:
+ using Factory = QDeferredFactory<std::remove_const_t<T>>;
+
+ QDeferredWeakPointer() = default;
+
+ QDeferredWeakPointer(const QDeferredSharedPointer<T> &strong)
+ : m_data(strong.m_data), m_factory(strong.m_factory)
+ {
+ }
+
+ QDeferredWeakPointer(QWeakPointer<T> data, QWeakPointer<Factory> factory)
+ : m_data(data), m_factory(factory)
+ {}
+
+ operator QWeakPointer<T>() const
+ {
+ if (m_factory) {
+ auto factory = m_factory.toStrongRef();
+ if (factory->isValid()) {
+ const_cast<std::remove_const_t<T> &>(*(m_data.toStrongRef())) = factory->create();
+ factory->clear();
+ }
+ }
+ return m_data;
+ }
+
+ operator QDeferredSharedPointer<T>() const
+ {
+ return QDeferredSharedPointer<T>(m_data.toStrongRef(), m_factory.toStrongRef());
+ }
+
+ operator QDeferredWeakPointer<const T>() const { return {m_data, m_factory}; }
+
+ QSharedPointer<T> toStrongRef() const
+ {
+ return QWeakPointer<T>(*this).toStrongRef();
+ }
+
+ bool isNull() const { return m_data.isNull(); }
+ explicit operator bool() const noexcept { return !isNull(); }
+ bool operator !() const noexcept { return isNull(); }
+
+ T *data() const { return QWeakPointer<T>(*this).data(); }
+
+ friend qsizetype qHash(const QDeferredWeakPointer &ptr, qsizetype seed = 0)
+ {
+ return qHashMulti(seed, ptr.m_factory, ptr.m_data);
+ }
+
+ friend bool operator==(const QDeferredWeakPointer &a, const QDeferredWeakPointer &b)
+ {
+ return a.m_factory == b.m_factory && a.m_data == b.m_data;
+ }
+
+private:
+ QWeakPointer<T> m_data;
+ QWeakPointer<Factory> m_factory;
+};
+
+
+QT_END_NAMESPACE
+
+#endif // QDEFERREDPOINTER_P_H
diff --git a/src/qmlcompiler/qmlcompiler.pro b/src/qmlcompiler/qmlcompiler.pro
index 111d27932a..37a1b44b9c 100644
--- a/src/qmlcompiler/qmlcompiler.pro
+++ b/src/qmlcompiler/qmlcompiler.pro
@@ -13,6 +13,7 @@ SOURCES = \
qqmljsstreamwriter.cpp
HEADERS = \
+ qdeferredpointer_p.h \
qqmljsresourcefilemapper_p.h \
qqmljsimportvisitor_p.h \
qqmljsimporter_p.h \
diff --git a/src/qmlcompiler/qqmljsimporter.cpp b/src/qmlcompiler/qqmljsimporter.cpp
index 717b559c44..0f334eabd3 100644
--- a/src/qmlcompiler/qqmljsimporter.cpp
+++ b/src/qmlcompiler/qqmljsimporter.cpp
@@ -303,21 +303,11 @@ QQmlJSScope::Ptr QQmlJSImporter::localFile2ScopeTree(const QString &filePath)
if (seen != m_importedFiles.end())
return *seen;
- QQmlJSTypeReader typeReader(filePath);
- QQmlJSScope::Ptr result = typeReader();
- m_importedFiles.insert(filePath, result);
-
- m_warnings.append(typeReader.errors());
-
- AvailableTypes types;
- types.qmlNames.insert(importDirectory(QFileInfo(filePath).canonicalPath()));
-
- const auto imports = typeReader.imports();
- for (const auto &import : imports)
- importHelper(import.module, &types, import.prefix, import.version);
-
- result->resolveTypes(types.qmlNames);
- return result;
+ return *m_importedFiles.insert(filePath, {
+ QQmlJSScope::create(),
+ QSharedPointer<QDeferredFactory<QQmlJSScope>>(
+ new QDeferredFactory<QQmlJSScope>(this, filePath))
+ });
}
QQmlJSScope::Ptr QQmlJSImporter::importFile(const QString &file)
@@ -339,9 +329,9 @@ QQmlJSImporter::ImportedTypes QQmlJSImporter::importDirectory(
it.next();
if (!it.fileName().front().isUpper())
continue; // Non-uppercase names cannot be imported anyway.
- QQmlJSScope::Ptr scope(localFile2ScopeTree(it.filePath()));
- if (!scope->internalName().isEmpty())
- qmlNames.insert(prefixedName(prefix, scope->internalName()), scope);
+
+ qmlNames.insert(prefixedName(prefix, QFileInfo(it.filePath()).baseName()),
+ localFile2ScopeTree(it.filePath()));
}
return qmlNames;
diff --git a/src/qmlcompiler/qqmljsimporter_p.h b/src/qmlcompiler/qqmljsimporter_p.h
index 1acd2a5ed1..3356997fd3 100644
--- a/src/qmlcompiler/qqmljsimporter_p.h
+++ b/src/qmlcompiler/qqmljsimporter_p.h
@@ -69,6 +69,8 @@ public:
}
private:
+ friend class QDeferredFactory<QQmlJSScope>;
+
struct AvailableTypes
{
// C++ names used in qmltypes files for non-composite types
diff --git a/src/qmlcompiler/qqmljsimportvisitor.cpp b/src/qmlcompiler/qqmljsimportvisitor.cpp
index 0b79339b62..0cb5632d40 100644
--- a/src/qmlcompiler/qqmljsimportvisitor.cpp
+++ b/src/qmlcompiler/qqmljsimportvisitor.cpp
@@ -43,7 +43,7 @@ QQmlJSScope::Ptr QQmlJSImportVisitor::result(const QString &scopeName) const
if (property.isAlias()) {
const auto it = m_objects.find(property.typeName());
if (it != m_objects.end())
- property.setType(*it);
+ property.setType(QQmlJSScope::ConstPtr(*it));
result->addProperty(property);
} else {
result->addProperty(property);
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index b2b9443fc6..e515431758 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -27,9 +27,12 @@
****************************************************************************/
#include "qqmljsscope_p.h"
+#include "qqmljstypereader_p.h"
+#include "qqmljsimporter_p.h"
#include <QtCore/qqueue.h>
#include <QtCore/qsharedpointer.h>
+#include <QtCore/qfileinfo.h>
#include <algorithm>
@@ -40,7 +43,7 @@ QQmlJSScope::QQmlJSScope(ScopeType type, const QQmlJSScope::Ptr &parentScope)
QQmlJSScope::Ptr QQmlJSScope::create(ScopeType type, const QQmlJSScope::Ptr &parentScope)
{
- QQmlJSScope::Ptr childScope(new QQmlJSScope{type, parentScope});
+ QSharedPointer<QQmlJSScope> childScope(new QQmlJSScope{type, parentScope});
if (parentScope) {
Q_ASSERT(type != QQmlJSScope::QMLScope
|| !parentScope->m_parentScope
@@ -142,7 +145,7 @@ void QQmlJSScope::resolveTypes(const QHash<QString, QQmlJSScope::ConstPtr> &cont
for (auto it = m_methods.begin(), end = m_methods.end(); it != end; ++it) {
it->setReturnType(findType(it->returnTypeName()));
const auto paramNames = it->parameterTypeNames();
- QList<QQmlJSScope::ConstPtr> paramTypes;
+ QList<QSharedPointer<const QQmlJSScope>> paramTypes;
for (const QString &paramName: paramNames)
paramTypes.append(findType(paramName));
@@ -183,4 +186,22 @@ bool QQmlJSScope::Export::isValid() const
return m_version.isValid() || !m_package.isEmpty() || !m_type.isEmpty();
}
+QQmlJSScope QDeferredFactory<QQmlJSScope>::create() const
+{
+ QQmlJSTypeReader typeReader(m_filePath);
+ QQmlJSScope::Ptr result = typeReader();
+
+ m_importer->m_warnings.append(typeReader.errors());
+
+ QQmlJSImporter::AvailableTypes types;
+ types.qmlNames.insert(m_importer->importDirectory(QFileInfo(m_filePath).canonicalPath()));
+
+ const auto imports = typeReader.imports();
+ for (const auto &import : imports)
+ m_importer->importHelper(import.module, &types, import.prefix, import.version);
+
+ result->resolveTypes(types.qmlNames);
+ return std::move(*result);
+}
+
QT_END_NAMESPACE
diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h
index 4cf1d4cd6c..df7aad840d 100644
--- a/src/qmlcompiler/qqmljsscope_p.h
+++ b/src/qmlcompiler/qqmljsscope_p.h
@@ -40,6 +40,7 @@
// We mean it.
#include "qqmljsmetatypes_p.h"
+#include "qdeferredpointer_p.h"
#include <QtQml/private/qqmljssourcelocation_p.h>
@@ -52,15 +53,45 @@
QT_BEGIN_NAMESPACE
+class QQmlJSImporter;
+class QQmlJSScope;
+
+template<>
+class QDeferredFactory<QQmlJSScope>
+{
+public:
+ QDeferredFactory(QQmlJSImporter *importer, const QString &filePath) :
+ m_filePath(filePath), m_importer(importer)
+ {}
+
+ QQmlJSScope create() const;
+
+ bool isValid() const
+ {
+ return !m_filePath.isEmpty() && m_importer != nullptr;
+ }
+
+ void clear() {
+ m_filePath.clear();
+ m_importer = nullptr;
+ }
+
+private:
+ QString m_filePath;
+ QQmlJSImporter *m_importer = nullptr;
+};
class QQmlJSScope
{
- Q_DISABLE_COPY_MOVE(QQmlJSScope)
+ Q_DISABLE_COPY(QQmlJSScope)
public:
- using Ptr = QSharedPointer<QQmlJSScope>;
- using WeakPtr = QWeakPointer<QQmlJSScope>;
- using ConstPtr = QSharedPointer<const QQmlJSScope>;
- using WeakConstPtr = QWeakPointer<const QQmlJSScope>;
+ QQmlJSScope(QQmlJSScope &&) = default;
+ QQmlJSScope &operator=(QQmlJSScope &&) = default;
+
+ using Ptr = QDeferredSharedPointer<QQmlJSScope>;
+ using WeakPtr = QDeferredWeakPointer<QQmlJSScope>;
+ using ConstPtr = QDeferredSharedPointer<const QQmlJSScope>;
+ using WeakConstPtr = QDeferredWeakPointer<const QQmlJSScope>;
enum ScopeType
{