aboutsummaryrefslogtreecommitdiffstats
path: root/tools/shared
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2020-03-30 17:42:48 +0200
committerUlf Hermann <ulf.hermann@qt.io>2020-04-01 10:29:29 +0200
commitc38ea80c5dd71a20eade7b3a3b619c1996c6af0b (patch)
treebd7eafa6e0e19f0aca3911c74ab99c7b8062a14a /tools/shared
parent4cf0962dc4d8d48aa600c5b56b160c8553782140 (diff)
Move qmllint's metatype support to tools/shared
We want to read qmltypes files and analyze scopes also from other tools. Furthermore, restructure the shared directory, so that each tool only includes what it needs. Change-Id: I96a2dcc8b1c5fac613592fb1867bf51fa5ef3a6e Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'tools/shared')
-rw-r--r--tools/shared/componentversion.cpp80
-rw-r--r--tools/shared/componentversion.h66
-rw-r--r--tools/shared/metatypes.h155
-rw-r--r--tools/shared/scopetree.cpp172
-rw-r--r--tools/shared/scopetree.h215
-rw-r--r--tools/shared/shared.pri26
-rw-r--r--tools/shared/typedescriptionreader.cpp658
-rw-r--r--tools/shared/typedescriptionreader.h95
8 files changed, 1463 insertions, 4 deletions
diff --git a/tools/shared/componentversion.cpp b/tools/shared/componentversion.cpp
new file mode 100644
index 0000000000..95403ec15f
--- /dev/null
+++ b/tools/shared/componentversion.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+#include "componentversion.h"
+#include <QtCore/qstring.h>
+
+ComponentVersion::ComponentVersion(const QString &versionString)
+{
+ const int dotIdx = versionString.indexOf(QLatin1Char('.'));
+ if (dotIdx == -1)
+ return;
+ bool ok = false;
+ const int maybeMajor = versionString.leftRef(dotIdx).toInt(&ok);
+ if (!ok)
+ return;
+ const int maybeMinor = versionString.midRef(dotIdx + 1).toInt(&ok);
+ if (!ok)
+ return;
+ m_version = QTypeRevision::fromVersion(maybeMajor, maybeMinor);
+}
+
+bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return lhs.version().majorVersion() < rhs.version().majorVersion()
+ || (lhs.version().majorVersion() == rhs.version().majorVersion()
+ && lhs.version().minorVersion() < rhs.version().minorVersion());
+}
+
+bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return lhs.version().majorVersion() < rhs.version().majorVersion()
+ || (lhs.version().majorVersion() == rhs.version().majorVersion()
+ && lhs.version().minorVersion() <= rhs.version().minorVersion());
+}
+
+bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return rhs < lhs;
+}
+
+bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return rhs <= lhs;
+}
+
+bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return lhs.version().majorVersion() == rhs.version().majorVersion()
+ && lhs.version().minorVersion() == rhs.version().minorVersion();
+}
+
+bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+{
+ return !(lhs == rhs);
+}
diff --git a/tools/shared/componentversion.h b/tools/shared/componentversion.h
new file mode 100644
index 0000000000..bbb039fc40
--- /dev/null
+++ b/tools/shared/componentversion.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 COMPONENTVERSION_H
+#define COMPONENTVERSION_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/qversionnumber.h>
+
+class ComponentVersion
+{
+public:
+ ComponentVersion() = default;
+ ComponentVersion(QTypeRevision version) : m_version(version) {}
+ explicit ComponentVersion(const QString &versionString);
+
+ QTypeRevision version() const { return m_version; }
+ bool isValid() const { return m_version.hasMajorVersion() && m_version.hasMinorVersion(); }
+
+private:
+ QTypeRevision m_version;
+};
+
+bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs);
+bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs);
+
+#endif // COMPONENTVERSION_H
diff --git a/tools/shared/metatypes.h b/tools/shared/metatypes.h
new file mode 100644
index 0000000000..d67de2edcd
--- /dev/null
+++ b/tools/shared/metatypes.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 METATYPES_H
+#define METATYPES_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/qstring.h>
+#include <QtCore/qstringlist.h>
+
+class MetaEnum
+{
+ QString m_name;
+ QStringList m_keys;
+
+public:
+ MetaEnum() = default;
+ explicit MetaEnum(QString name) : m_name(std::move(name)) {}
+
+ bool isValid() const { return !m_name.isEmpty(); }
+
+ QString name() const { return m_name; }
+ void setName(const QString &name) { m_name = name; }
+
+ void addKey(const QString &key) { m_keys.append(key); }
+ QStringList keys() const { return m_keys; }
+};
+
+class MetaMethod
+{
+public:
+ enum Type {
+ Signal,
+ Slot,
+ Method
+ };
+
+ enum Access {
+ Private,
+ Protected,
+ Public
+ };
+
+ MetaMethod() = default;
+ explicit MetaMethod(QString name, QString returnType = QString())
+ : m_name(std::move(name))
+ , m_returnType(std::move(returnType))
+ , m_methodType(Method)
+ , m_methodAccess(Public)
+ {}
+
+ QString methodName() const { return m_name; }
+ void setMethodName(const QString &name) { m_name = name; }
+
+ void setReturnType(const QString &type) { m_returnType = type; }
+
+ QStringList parameterNames() const { return m_paramNames; }
+ QStringList parameterTypes() const { return m_paramTypes; }
+ void addParameter(const QString &name, const QString &type)
+ {
+ m_paramNames.append(name);
+ m_paramTypes.append(type);
+ }
+
+ int methodType() const { return m_methodType; }
+ void setMethodType(Type methodType) { m_methodType = methodType; }
+
+ Access access() const { return m_methodAccess; }
+
+ int revision() const { return m_revision; }
+ void setRevision(int r) { m_revision = r; }
+
+private:
+ QString m_name;
+ QString m_returnType;
+ QStringList m_paramNames;
+ QStringList m_paramTypes;
+ Type m_methodType = Signal;
+ Access m_methodAccess = Private;
+ int m_revision = 0;
+};
+
+class ScopeTree;
+class MetaProperty
+{
+ QString m_propertyName;
+ QString m_typeName;
+ const ScopeTree *m_type = nullptr;
+ bool m_isList;
+ bool m_isWritable;
+ bool m_isPointer;
+ bool m_isAlias;
+ int m_revision;
+
+public:
+ MetaProperty(QString propertyName, QString typeName,
+ bool isList, bool isWritable, bool isPointer, bool isAlias,
+ int revision)
+ : m_propertyName(std::move(propertyName))
+ , m_typeName(std::move(typeName))
+ , m_isList(isList)
+ , m_isWritable(isWritable)
+ , m_isPointer(isPointer)
+ , m_isAlias(isAlias)
+ , m_revision(revision)
+ {}
+
+ QString propertyName() const { return m_propertyName; }
+ QString typeName() const { return m_typeName; }
+
+ void setType(const ScopeTree *type) { m_type = type; }
+ const ScopeTree *type() const { return m_type; }
+
+ bool isList() const { return m_isList; }
+ bool isWritable() const { return m_isWritable; }
+ bool isPointer() const { return m_isPointer; }
+ bool isAlias() const { return m_isAlias; }
+ int revision() const { return m_revision; }
+};
+
+#endif // METATYPES_H
diff --git a/tools/shared/scopetree.cpp b/tools/shared/scopetree.cpp
new file mode 100644
index 0000000000..870ba13fc4
--- /dev/null
+++ b/tools/shared/scopetree.cpp
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+#include "scopetree.h"
+
+#include <QtCore/qqueue.h>
+#include <QtCore/qsharedpointer.h>
+
+#include <algorithm>
+
+ScopeTree::ScopeTree(ScopeType type, QString name, ScopeTree *parentScope)
+ : m_parentScope(parentScope), m_name(std::move(name)), m_scopeType(type) {}
+
+ScopeTree::Ptr ScopeTree::createNewChildScope(ScopeType type, const QString &name)
+{
+ Q_ASSERT(type != ScopeType::QMLScope
+ || !m_parentScope
+ || m_parentScope->m_scopeType == ScopeType::QMLScope
+ || m_parentScope->m_name == QLatin1String("global"));
+ auto childScope = ScopeTree::Ptr(new ScopeTree{type, name, this});
+ m_childScopes.push_back(childScope);
+ return childScope;
+}
+
+void ScopeTree::insertJSIdentifier(const QString &id, ScopeType scope)
+{
+ Q_ASSERT(m_scopeType != ScopeType::QMLScope);
+ Q_ASSERT(scope != ScopeType::QMLScope);
+ if (scope == ScopeType::JSFunctionScope) {
+ auto targetScope = this;
+ while (targetScope->scopeType() != ScopeType::JSFunctionScope)
+ targetScope = targetScope->m_parentScope;
+ targetScope->m_jsIdentifiers.insert(id);
+ } else {
+ m_jsIdentifiers.insert(id);
+ }
+}
+
+void ScopeTree::insertSignalIdentifier(const QString &id, const MetaMethod &method,
+ const QQmlJS::SourceLocation &loc,
+ bool hasMultilineHandlerBody)
+{
+ Q_ASSERT(m_scopeType == ScopeType::QMLScope);
+ m_injectedSignalIdentifiers.insert(id, {method, loc, hasMultilineHandlerBody});
+}
+
+void ScopeTree::insertPropertyIdentifier(const MetaProperty &property)
+{
+ addProperty(property);
+ MetaMethod method(property.propertyName() + QLatin1String("Changed"), QLatin1String("void"));
+ addMethod(method);
+}
+
+void ScopeTree::addUnmatchedSignalHandler(const QString &handler,
+ const QQmlJS::SourceLocation &location)
+{
+ m_unmatchedSignalHandlers.append(qMakePair(handler, location));
+}
+
+bool ScopeTree::isIdInCurrentScope(const QString &id) const
+{
+ return isIdInCurrentQMlScopes(id) || isIdInCurrentJSScopes(id);
+}
+
+void ScopeTree::addIdToAccessed(const QString &id, const QQmlJS::SourceLocation &location) {
+ m_memberAccessChains.append(QVector<FieldMember>());
+ m_memberAccessChains.last().append(FieldMember {id, QString(), location});
+}
+
+void ScopeTree::accessMember(const QString &name, const QString &parentType,
+ const QQmlJS::SourceLocation &location)
+{
+ Q_ASSERT(!m_memberAccessChains.last().isEmpty());
+ m_memberAccessChains.last().append(FieldMember {name, parentType, location });
+}
+
+bool ScopeTree::isVisualRootScope() const
+{
+ return m_parentScope && m_parentScope->m_parentScope
+ && m_parentScope->m_parentScope->m_parentScope == nullptr;
+}
+
+bool ScopeTree::isIdInCurrentQMlScopes(const QString &id) const
+{
+ const auto *qmlScope = currentQMLScope();
+ return qmlScope->m_properties.contains(id)
+ || qmlScope->m_methods.contains(id)
+ || qmlScope->m_enums.contains(id);
+}
+
+bool ScopeTree::isIdInCurrentJSScopes(const QString &id) const
+{
+ auto jsScope = this;
+ while (jsScope) {
+ if (jsScope->m_scopeType != ScopeType::QMLScope && jsScope->m_jsIdentifiers.contains(id))
+ return true;
+ jsScope = jsScope->m_parentScope;
+ }
+ return false;
+}
+
+bool ScopeTree::isIdInjectedFromSignal(const QString &id) const
+{
+ return currentQMLScope()->m_injectedSignalIdentifiers.contains(id);
+}
+
+const ScopeTree *ScopeTree::currentQMLScope() const
+{
+ auto qmlScope = this;
+ while (qmlScope && qmlScope->m_scopeType != ScopeType::QMLScope)
+ qmlScope = qmlScope->m_parentScope;
+ return qmlScope;
+}
+
+void ScopeTree::addExport(const QString &name, const QString &package,
+ const ComponentVersion &version)
+{
+ m_exports.append(Export(package, name, version, 0));
+}
+
+void ScopeTree::setExportMetaObjectRevision(int exportIndex, int metaObjectRevision)
+{
+ m_exports[exportIndex].setMetaObjectRevision(metaObjectRevision);
+}
+
+void ScopeTree::updateParentProperty(const ScopeTree *scope)
+{
+ auto it = m_properties.find(QLatin1String("parent"));
+ if (it != m_properties.end()
+ && scope->name() != QLatin1String("Component")
+ && scope->name() != QLatin1String("program"))
+ it->setType(scope);
+}
+
+ScopeTree::Export::Export(QString package, QString type, const ComponentVersion &version,
+ int metaObjectRevision) :
+ m_package(std::move(package)),
+ m_type(std::move(type)),
+ m_version(version),
+ m_metaObjectRevision(metaObjectRevision)
+{
+}
+
+bool ScopeTree::Export::isValid() const
+{
+ return m_version.isValid() || !m_package.isEmpty() || !m_type.isEmpty();
+}
diff --git a/tools/shared/scopetree.h b/tools/shared/scopetree.h
new file mode 100644
index 0000000000..e4e6a59ac2
--- /dev/null
+++ b/tools/shared/scopetree.h
@@ -0,0 +1,215 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 SCOPETREE_H
+#define SCOPETREE_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 "metatypes.h"
+#include "componentversion.h"
+
+#include <QtQml/private/qqmljssourcelocation_p.h>
+
+#include <QtCore/qset.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qstring.h>
+
+enum class ScopeType
+{
+ JSFunctionScope,
+ JSLexicalScope,
+ QMLScope
+};
+
+struct MethodUsage
+{
+ MetaMethod method;
+ QQmlJS::SourceLocation loc;
+ bool hasMultilineHandlerBody;
+};
+
+class ScopeTree
+{
+ Q_DISABLE_COPY_MOVE(ScopeTree)
+public:
+ using Ptr = QSharedPointer<ScopeTree>;
+ using ConstPtr = QSharedPointer<const ScopeTree>;
+
+ class Export {
+ public:
+ Export() = default;
+ Export(QString package, QString type, const ComponentVersion &version,
+ int metaObjectRevision);
+
+ bool isValid() const;
+
+ int metaObjectRevision() const { return m_metaObjectRevision; }
+ void setMetaObjectRevision(int metaObjectRevision)
+ {
+ m_metaObjectRevision = metaObjectRevision;
+ }
+
+ QString package() const { return m_package; }
+ QString type() const { return m_type; }
+
+ private:
+ QString m_package;
+ QString m_type;
+ ComponentVersion m_version;
+ int m_metaObjectRevision = 0;
+ };
+
+ ScopeTree(ScopeType type, QString name = QString(),
+ ScopeTree *parentScope = nullptr);
+
+ ScopeTree::Ptr createNewChildScope(ScopeType type, const QString &name);
+ ScopeTree *parentScope() const { return m_parentScope; }
+
+ void insertJSIdentifier(const QString &id, ScopeType scope);
+ void insertSignalIdentifier(const QString &id, const MetaMethod &method,
+ const QQmlJS::SourceLocation &loc, bool hasMultilineHandlerBody);
+ // inserts property as qml identifier as well as the corresponding
+ void insertPropertyIdentifier(const MetaProperty &prop);
+ void addUnmatchedSignalHandler(const QString &handler,
+ const QQmlJS::SourceLocation &location);
+
+ bool isIdInCurrentScope(const QString &id) const;
+ void addIdToAccessed(const QString &id, const QQmlJS::SourceLocation &location);
+ void accessMember(const QString &name, const QString &parentType,
+ const QQmlJS::SourceLocation &location);
+
+ bool isVisualRootScope() const;
+ QString name() const { return m_name; }
+
+ ScopeType scopeType() const { return m_scopeType; }
+
+ void addMethods(const QHash<QString, MetaMethod> &methods) { m_methods.insert(methods); }
+ void addMethod(const MetaMethod &method) { m_methods.insert(method.methodName(), method); }
+ QHash<QString, MetaMethod> methods() const { return m_methods; }
+
+ void addEnum(const MetaEnum &fakeEnum) { m_enums.insert(fakeEnum.name(), fakeEnum); }
+ QHash<QString, MetaEnum> enums() const { return m_enums; }
+
+ QString className() const { return m_className; }
+ void setClassName(const QString &name) { m_className = name; }
+
+ void addExport(const QString &name, const QString &package, const ComponentVersion &version);
+ void setExportMetaObjectRevision(int exportIndex, int metaObjectRevision);
+ QList<Export> exports() const { return m_exports; }
+
+ void setSuperclassName(const QString &superclass) { m_superName = superclass; }
+ QString superclassName() const { return m_superName; }
+
+ void addProperty(const MetaProperty &prop) { m_properties.insert(prop.propertyName(), prop); }
+ QHash<QString, MetaProperty> properties() const { return m_properties; }
+ void updateParentProperty(const ScopeTree *scope);
+
+ QString defaultPropertyName() const { return m_defaultPropertyName; }
+ void setDefaultPropertyName(const QString &name) { m_defaultPropertyName = name; }
+
+ QString attachedTypeName() const { return m_attachedTypeName; }
+ void setAttachedTypeName(const QString &name) { m_attachedTypeName = name; }
+
+ bool isSingleton() const { return m_isSingleton; }
+ bool isCreatable() const { return m_isCreatable; }
+ bool isComposite() const { return m_isComposite; }
+ void setIsSingleton(bool value) { m_isSingleton = value; }
+ void setIsCreatable(bool value) { m_isCreatable = value; }
+ void setIsComposite(bool value) { m_isSingleton = value; }
+
+ struct FieldMember
+ {
+ QString m_name;
+ QString m_parentType;
+ QQmlJS::SourceLocation m_location;
+ };
+
+ QVector<QPair<QString, QQmlJS::SourceLocation>> unmatchedSignalHandlers() const
+ {
+ return m_unmatchedSignalHandlers;
+ }
+
+ QVector<QVector<FieldMember>> memberAccessChains() const
+ {
+ return m_memberAccessChains;
+ }
+
+ bool isIdInCurrentQMlScopes(const QString &id) const;
+ bool isIdInCurrentJSScopes(const QString &id) const;
+ bool isIdInjectedFromSignal(const QString &id) const;
+ const ScopeTree *currentQMLScope() const;
+
+ QVector<ScopeTree::Ptr> childScopes() const
+ {
+ return m_childScopes;
+ }
+
+ QMultiHash<QString, MethodUsage> injectedSignalIdentifiers() const
+ {
+ return m_injectedSignalIdentifiers;
+ }
+
+private:
+
+ QSet<QString> m_jsIdentifiers;
+ QMultiHash<QString, MethodUsage> m_injectedSignalIdentifiers;
+
+ QHash<QString, MetaMethod> m_methods;
+ QHash<QString, MetaProperty> m_properties;
+ QHash<QString, MetaEnum> m_enums;
+
+ QVector<QVector<FieldMember>> m_memberAccessChains;
+ QVector<QPair<QString, QQmlJS::SourceLocation>> m_unmatchedSignalHandlers;
+
+ QVector<ScopeTree::Ptr> m_childScopes;
+ ScopeTree *m_parentScope;
+
+ QString m_name;
+ QString m_className;
+ QString m_superName;
+
+ ScopeType m_scopeType = ScopeType::QMLScope;
+ QList<Export> m_exports;
+
+ QString m_defaultPropertyName;
+ QString m_attachedTypeName;
+ bool m_isSingleton = false;
+ bool m_isCreatable = true;
+ bool m_isComposite = false;
+};
+
+#endif // SCOPETREE_H
diff --git a/tools/shared/shared.pri b/tools/shared/shared.pri
index 1438c3b3da..1b3cd4d37b 100644
--- a/tools/shared/shared.pri
+++ b/tools/shared/shared.pri
@@ -1,9 +1,27 @@
INCLUDEPATH += $$PWD
-SOURCES += \
- $$PWD/resourcefilemapper.cpp \
+# The relevant tools need different bits and pieces.
+# Furthermore, some of the classes require devtools, some not.
+
+RESOURCEFILEMAPPER_SOURCES = \
+ $$PWD/resourcefilemapper.cpp
+
+RESOURCEFILEMAPPER_HEADERS = \
+ $$PWD/resourcefilemapper.h
+
+METATYPEREADER_SOURCES = \
+ $$PWD/componentversion.cpp \
+ $$PWD/scopetree.cpp \
+ $$PWD/typedescriptionreader.cpp
+
+METATYPEREADER_HEADERS = \
+ $$PWD/componentversion.h \
+ $$PWD/metatypes.h \
+ $$PWD/scopetree.h \
+ $$PWD/typedescriptionreader.h
+
+QMLSTREAMWRITER_SOURCES = \
$$PWD/qmlstreamwriter.cpp
-HEADERS += \
- $$PWD/resourcefilemapper.h \
+QMLSTREAMWRITER_HEADERS = \
$$PWD/qmlstreamwriter.h
diff --git a/tools/shared/typedescriptionreader.cpp b/tools/shared/typedescriptionreader.cpp
new file mode 100644
index 0000000000..cc623b8288
--- /dev/null
+++ b/tools/shared/typedescriptionreader.cpp
@@ -0,0 +1,658 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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$
+**
+****************************************************************************/
+
+#include "typedescriptionreader.h"
+
+#include <QtQml/private/qqmljsparser_p.h>
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+
+#include <QtCore/qdir.h>
+
+using namespace QQmlJS;
+using namespace QQmlJS::AST;
+
+QString toString(const UiQualifiedId *qualifiedId, QChar delimiter = QLatin1Char('.'))
+{
+ QString result;
+
+ for (const UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) {
+ if (iter != qualifiedId)
+ result += delimiter;
+
+ result += iter->name;
+ }
+
+ return result;
+}
+
+bool TypeDescriptionReader::operator()(
+ QHash<QString, ScopeTree::ConstPtr> *objects,
+ QStringList *dependencies)
+{
+ Engine engine;
+
+ Lexer lexer(&engine);
+ Parser parser(&engine);
+
+ lexer.setCode(m_source, /*lineno = */ 1, /*qmlMode = */true);
+
+ if (!parser.parse()) {
+ m_errorMessage = QString::fromLatin1("%1:%2: %3").arg(
+ QString::number(parser.errorLineNumber()),
+ QString::number(parser.errorColumnNumber()),
+ parser.errorMessage());
+ return false;
+ }
+
+ m_objects = objects;
+ m_dependencies = dependencies;
+ readDocument(parser.ast());
+
+ return m_errorMessage.isEmpty();
+}
+
+void TypeDescriptionReader::readDocument(UiProgram *ast)
+{
+ if (!ast) {
+ addError(SourceLocation(), tr("Could not parse document."));
+ return;
+ }
+
+ if (!ast->headers || ast->headers->next || !cast<UiImport *>(ast->headers->headerItem)) {
+ addError(SourceLocation(), tr("Expected a single import."));
+ return;
+ }
+
+ auto *import = cast<UiImport *>(ast->headers->headerItem);
+ if (toString(import->importUri) != QLatin1String("QtQuick.tooling")) {
+ addError(import->importToken, tr("Expected import of QtQuick.tooling."));
+ return;
+ }
+
+ if (!import->version) {
+ addError(import->firstSourceLocation(), tr("Import statement without version."));
+ return;
+ }
+
+ if (import->version->version.majorVersion() != 1) {
+ addError(import->version->firstSourceLocation(),
+ tr("Major version different from 1 not supported."));
+ return;
+ }
+
+ if (!ast->members || !ast->members->member || ast->members->next) {
+ addError(SourceLocation(), tr("Expected document to contain a single object definition."));
+ return;
+ }
+
+ auto *module = cast<UiObjectDefinition *>(ast->members->member);
+ if (!module) {
+ addError(SourceLocation(), tr("Expected document to contain a single object definition."));
+ return;
+ }
+
+ if (toString(module->qualifiedTypeNameId) != QLatin1String("Module")) {
+ addError(SourceLocation(), tr("Expected document to contain a Module {} member."));
+ return;
+ }
+
+ readModule(module);
+}
+
+void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
+{
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ auto *component = cast<UiObjectDefinition *>(member);
+
+ auto *script = cast<UiScriptBinding *>(member);
+ if (script && (toString(script->qualifiedId) == QStringLiteral("dependencies"))) {
+ readDependencies(script);
+ continue;
+ }
+
+ QString typeName;
+ if (component)
+ typeName = toString(component->qualifiedTypeNameId);
+
+ if (!component || typeName != QLatin1String("Component")) {
+ continue;
+ }
+
+ if (typeName == QLatin1String("Component"))
+ readComponent(component);
+ }
+}
+
+void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
+{
+ m_errorMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
+ QDir::toNativeSeparators(m_fileName),
+ QString::number(loc.startLine),
+ QString::number(loc.startColumn),
+ message);
+}
+
+void TypeDescriptionReader::addWarning(const SourceLocation &loc, const QString &message)
+{
+ m_warningMessage += QString::fromLatin1("%1:%2:%3: %4\n").arg(
+ QDir::toNativeSeparators(m_fileName),
+ QString::number(loc.startLine),
+ QString::number(loc.startColumn),
+ message);
+}
+
+void TypeDescriptionReader::readDependencies(UiScriptBinding *ast)
+{
+ auto *stmt = cast<ExpressionStatement*>(ast->statement);
+ if (!stmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected dependency definitions"));
+ return;
+ }
+ auto *exp = cast<ArrayPattern *>(stmt->expression);
+ if (!exp) {
+ addError(stmt->expression->firstSourceLocation(), tr("Expected dependency definitions"));
+ return;
+ }
+ for (PatternElementList *l = exp->elements; l; l = l->next) {
+ auto *str = cast<StringLiteral *>(l->element->initializer);
+ *m_dependencies << str->value.toString();
+ }
+}
+
+void TypeDescriptionReader::readComponent(UiObjectDefinition *ast)
+{
+ ScopeTree::Ptr scope(new ScopeTree(ScopeType::QMLScope));
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ auto *component = cast<UiObjectDefinition *>(member);
+ auto *script = cast<UiScriptBinding *>(member);
+ if (component) {
+ QString name = toString(component->qualifiedTypeNameId);
+ if (name == QLatin1String("Property"))
+ readProperty(component, scope);
+ else if (name == QLatin1String("Method") || name == QLatin1String("Signal"))
+ readSignalOrMethod(component, name == QLatin1String("Method"), scope);
+ else if (name == QLatin1String("Enum"))
+ readEnum(component, scope);
+ else
+ addWarning(component->firstSourceLocation(),
+ tr("Expected only Property, Method, Signal and Enum object definitions, "
+ "not \"%1\".").arg(name));
+ } else if (script) {
+ QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("name")) {
+ scope->setClassName(readStringBinding(script));
+ } else if (name == QLatin1String("prototype")) {
+ scope->setSuperclassName(readStringBinding(script));
+ } else if (name == QLatin1String("defaultProperty")) {
+ scope->setDefaultPropertyName(readStringBinding(script));
+ } else if (name == QLatin1String("exports")) {
+ readExports(script, scope);
+ } else if (name == QLatin1String("exportMetaObjectRevisions")) {
+ readMetaObjectRevisions(script, scope);
+ } else if (name == QLatin1String("attachedType")) {
+ scope->setAttachedTypeName(readStringBinding(script));
+ } else if (name == QLatin1String("isSingleton")) {
+ scope->setIsSingleton(readBoolBinding(script));
+ } else if (name == QLatin1String("isCreatable")) {
+ scope->setIsCreatable(readBoolBinding(script));
+ } else if (name == QLatin1String("isComposite")) {
+ scope->setIsComposite(readBoolBinding(script));
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Expected only name, prototype, defaultProperty, attachedType, "
+ "exports, isSingleton, isCreatable, isComposite and "
+ "exportMetaObjectRevisions script bindings, not \"%1\".").arg(name));
+ }
+ } else {
+ addWarning(member->firstSourceLocation(),
+ tr("Expected only script bindings and object definitions."));
+ }
+ }
+
+ if (scope->className().isEmpty()) {
+ addError(ast->firstSourceLocation(), tr("Component definition is missing a name binding."));
+ return;
+ }
+
+ // ### add implicit export into the package of c++ types
+ scope->addExport(scope->className(), QStringLiteral("<cpp>"), ComponentVersion());
+ m_objects->insert(scope->className(), scope);
+}
+
+void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod,
+ const ScopeTree::Ptr &scope)
+{
+ MetaMethod metaMethod;
+ // ### confusion between Method and Slot. Method should be removed.
+ if (isMethod)
+ metaMethod.setMethodType(MetaMethod::Slot);
+ else
+ metaMethod.setMethodType(MetaMethod::Signal);
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ auto *component = cast<UiObjectDefinition *>(member);
+ auto *script = cast<UiScriptBinding *>(member);
+ if (component) {
+ QString name = toString(component->qualifiedTypeNameId);
+ if (name == QLatin1String("Parameter")) {
+ readParameter(component, &metaMethod);
+ } else {
+ addWarning(component->firstSourceLocation(),
+ tr("Expected only Parameter object definitions."));
+ }
+ } else if (script) {
+ QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("name")) {
+ metaMethod.setMethodName(readStringBinding(script));
+ } else if (name == QLatin1String("type")) {
+ metaMethod.setReturnType(readStringBinding(script));
+ } else if (name == QLatin1String("revision")) {
+ metaMethod.setRevision(readIntBinding(script));
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Expected only name and type script bindings."));
+ }
+ } else {
+ addWarning(member->firstSourceLocation(),
+ tr("Expected only script bindings and object definitions."));
+ }
+ }
+
+ if (metaMethod.methodName().isEmpty()) {
+ addError(ast->firstSourceLocation(),
+ tr("Method or signal is missing a name script binding."));
+ return;
+ }
+
+ scope->addMethod(metaMethod);
+}
+
+void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, const ScopeTree::Ptr &scope)
+{
+ QString name;
+ QString type;
+ bool isPointer = false;
+ bool isReadonly = false;
+ bool isList = false;
+ int revision = 0;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ auto *script = cast<UiScriptBinding *>(member);
+ if (!script) {
+ addWarning(member->firstSourceLocation(), tr("Expected script binding."));
+ continue;
+ }
+
+ QString id = toString(script->qualifiedId);
+ if (id == QLatin1String("name")) {
+ name = readStringBinding(script);
+ } else if (id == QLatin1String("type")) {
+ type = readStringBinding(script);
+ } else if (id == QLatin1String("isPointer")) {
+ isPointer = readBoolBinding(script);
+ } else if (id == QLatin1String("isReadonly")) {
+ isReadonly = readBoolBinding(script);
+ } else if (id == QLatin1String("isList")) {
+ isList = readBoolBinding(script);
+ } else if (id == QLatin1String("revision")) {
+ revision = readIntBinding(script);
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Expected only type, name, revision, isPointer, isReadonly and"
+ " isList script bindings."));
+ }
+ }
+
+ if (name.isEmpty() || type.isEmpty()) {
+ addError(ast->firstSourceLocation(),
+ tr("Property object is missing a name or type script binding."));
+ return;
+ }
+
+ scope->addProperty(MetaProperty(name, type, isList, !isReadonly, isPointer, false, revision));
+}
+
+void TypeDescriptionReader::readEnum(UiObjectDefinition *ast, const ScopeTree::Ptr &scope)
+{
+ MetaEnum metaEnum;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ auto *script = cast<UiScriptBinding *>(member);
+ if (!script) {
+ addWarning(member->firstSourceLocation(), tr("Expected script binding."));
+ continue;
+ }
+
+ QString name = toString(script->qualifiedId);
+ if (name == QLatin1String("name")) {
+ metaEnum.setName(readStringBinding(script));
+ } else if (name == QLatin1String("values")) {
+ readEnumValues(script, &metaEnum);
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Expected only name and values script bindings."));
+ }
+ }
+
+ scope->addEnum(metaEnum);
+}
+
+void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, MetaMethod *metaMethod)
+{
+ QString name;
+ QString type;
+
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+ auto *script = cast<UiScriptBinding *>(member);
+ if (!script) {
+ addWarning(member->firstSourceLocation(), tr("Expected script binding."));
+ continue;
+ }
+
+ const QString id = toString(script->qualifiedId);
+ if (id == QLatin1String("name")) {
+ name = readStringBinding(script);
+ } else if (id == QLatin1String("type")) {
+ type = readStringBinding(script);
+ } else if (id == QLatin1String("isPointer")) {
+ // ### unhandled
+ } else if (id == QLatin1String("isReadonly")) {
+ // ### unhandled
+ } else if (id == QLatin1String("isList")) {
+ // ### unhandled
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Expected only name and type script bindings."));
+ }
+ }
+
+ metaMethod->addParameter(name, type);
+}
+
+QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
+{
+ Q_ASSERT(ast);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected string after colon."));
+ return QString();
+ }
+
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected string after colon."));
+ return QString();
+ }
+
+ auto *stringLit = cast<StringLiteral *>(expStmt->expression);
+ if (!stringLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected string after colon."));
+ return QString();
+ }
+
+ return stringLit->value.toString();
+}
+
+bool TypeDescriptionReader::readBoolBinding(UiScriptBinding *ast)
+{
+ Q_ASSERT(ast);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected boolean after colon."));
+ return false;
+ }
+
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon."));
+ return false;
+ }
+
+ auto *trueLit = cast<TrueLiteral *>(expStmt->expression);
+ auto *falseLit = cast<FalseLiteral *>(expStmt->expression);
+ if (!trueLit && !falseLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected true or false after colon."));
+ return false;
+ }
+
+ return trueLit;
+}
+
+double TypeDescriptionReader::readNumericBinding(UiScriptBinding *ast)
+{
+ Q_ASSERT(ast);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected numeric literal after colon."));
+ return 0;
+ }
+
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(),
+ tr("Expected numeric literal after colon."));
+ return 0;
+ }
+
+ auto *numericLit = cast<NumericLiteral *>(expStmt->expression);
+ if (!numericLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return 0;
+ }
+
+ return numericLit->value;
+}
+
+ComponentVersion TypeDescriptionReader::readNumericVersionBinding(UiScriptBinding *ast)
+{
+ ComponentVersion invalidVersion;
+
+ if (!ast || !ast->statement) {
+ addError((ast ? ast->colonToken : SourceLocation()),
+ tr("Expected numeric literal after colon."));
+ return invalidVersion;
+ }
+
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(),
+ tr("Expected numeric literal after colon."));
+ return invalidVersion;
+ }
+
+ auto *numericLit = cast<NumericLiteral *>(expStmt->expression);
+ if (!numericLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected numeric literal after colon."));
+ return invalidVersion;
+ }
+
+ return ComponentVersion(m_source.mid(numericLit->literalToken.begin(),
+ numericLit->literalToken.length));
+}
+
+int TypeDescriptionReader::readIntBinding(UiScriptBinding *ast)
+{
+ double v = readNumericBinding(ast);
+ int i = static_cast<int>(v);
+
+ if (i != v) {
+ addError(ast->firstSourceLocation(), tr("Expected integer after colon."));
+ return 0;
+ }
+
+ return i;
+}
+
+void TypeDescriptionReader::readExports(UiScriptBinding *ast, const ScopeTree::Ptr &scope)
+{
+ Q_ASSERT(ast);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected array of strings after colon."));
+ return;
+ }
+
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(),
+ tr("Expected array of strings after colon."));
+ return;
+ }
+
+ auto *arrayLit = cast<ArrayPattern *>(expStmt->expression);
+ if (!arrayLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected array of strings after colon."));
+ return;
+ }
+
+ for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
+ auto *stringLit = cast<StringLiteral *>(it->element->initializer);
+ if (!stringLit) {
+ addError(arrayLit->firstSourceLocation(),
+ tr("Expected array literal with only string literal members."));
+ return;
+ }
+ QString exp = stringLit->value.toString();
+ int slashIdx = exp.indexOf(QLatin1Char('/'));
+ int spaceIdx = exp.indexOf(QLatin1Char(' '));
+ ComponentVersion version(exp.mid(spaceIdx + 1));
+
+ if (spaceIdx == -1 || !version.isValid()) {
+ addError(stringLit->firstSourceLocation(),
+ tr("Expected string literal to contain 'Package/Name major.minor' "
+ "or 'Name major.minor'."));
+ continue;
+ }
+ QString package;
+ if (slashIdx != -1)
+ package = exp.left(slashIdx);
+ QString name = exp.mid(slashIdx + 1, spaceIdx - (slashIdx+1));
+
+ // ### relocatable exports where package is empty?
+ scope->addExport(name, package, version);
+ }
+}
+
+void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast,
+ const ScopeTree::Ptr &scope)
+{
+ Q_ASSERT(ast);
+
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected array of numbers after colon."));
+ return;
+ }
+
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(),
+ tr("Expected array of numbers after colon."));
+ return;
+ }
+
+ auto *arrayLit = cast<ArrayPattern *>(expStmt->expression);
+ if (!arrayLit) {
+ addError(expStmt->firstSourceLocation(), tr("Expected array of numbers after colon."));
+ return;
+ }
+
+ int exportIndex = 0;
+ const int exportCount = scope->exports().size();
+ for (PatternElementList *it = arrayLit->elements; it; it = it->next, ++exportIndex) {
+ auto *numberLit = cast<NumericLiteral *>(it->element->initializer);
+ if (!numberLit) {
+ addError(arrayLit->firstSourceLocation(),
+ tr("Expected array literal with only number literal members."));
+ return;
+ }
+
+ if (exportIndex >= exportCount) {
+ addError(numberLit->firstSourceLocation(),
+ tr("Meta object revision without matching export."));
+ return;
+ }
+
+ const double v = numberLit->value;
+ const int metaObjectRevision = static_cast<int>(v);
+ if (metaObjectRevision != v) {
+ addError(numberLit->firstSourceLocation(), tr("Expected integer."));
+ return;
+ }
+
+ scope->setExportMetaObjectRevision(exportIndex, metaObjectRevision);
+ }
+}
+
+void TypeDescriptionReader::readEnumValues(UiScriptBinding *ast, MetaEnum *metaEnum)
+{
+ if (!ast)
+ return;
+ if (!ast->statement) {
+ addError(ast->colonToken, tr("Expected object literal after colon."));
+ return;
+ }
+
+ auto *expStmt = cast<ExpressionStatement *>(ast->statement);
+ if (!expStmt) {
+ addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon."));
+ return;
+ }
+
+ if (auto *objectLit = cast<ObjectPattern *>(expStmt->expression)) {
+ for (PatternPropertyList *it = objectLit->properties; it; it = it->next) {
+ if (PatternProperty *assignement = it->property) {
+ if (auto *name = cast<StringLiteralPropertyName *>(assignement->name)) {
+ metaEnum->addKey(name->id.toString());
+ continue;
+ }
+ }
+ addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
+ }
+ } else if (auto *arrayLit = cast<ArrayPattern *>(expStmt->expression)) {
+ for (PatternElementList *it = arrayLit->elements; it; it = it->next) {
+ if (PatternElement *element = it->element) {
+ if (auto *name = cast<StringLiteral *>(element->initializer)) {
+ metaEnum->addKey(name->value.toString());
+ continue;
+ }
+ }
+ addError(it->firstSourceLocation(), tr("Expected strings as enum keys."));
+ }
+ } else {
+ addError(ast->statement->firstSourceLocation(),
+ tr("Expected either array or object literal as enum definition."));
+ }
+}
diff --git a/tools/shared/typedescriptionreader.h b/tools/shared/typedescriptionreader.h
new file mode 100644
index 0000000000..2c86282163
--- /dev/null
+++ b/tools/shared/typedescriptionreader.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 TYPEDESCRIPTIONREADER_H
+#define TYPEDESCRIPTIONREADER_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 "scopetree.h"
+
+#include <QtQml/private/qqmljsastfwd_p.h>
+
+// for Q_DECLARE_TR_FUNCTIONS
+#include <QtCore/qcoreapplication.h>
+
+class TypeDescriptionReader
+{
+ Q_DECLARE_TR_FUNCTIONS(TypeDescriptionReader)
+public:
+ TypeDescriptionReader() = default;
+ explicit TypeDescriptionReader(QString fileName, QString data)
+ : m_fileName(std::move(fileName)), m_source(std::move(data)) {}
+
+ bool operator()(
+ QHash<QString, ScopeTree::ConstPtr> *objects,
+ QStringList *dependencies);
+
+ QString errorMessage() const { return m_errorMessage; }
+ QString warningMessage() const { return m_warningMessage; }
+
+private:
+ void readDocument(QQmlJS::AST::UiProgram *ast);
+ void readModule(QQmlJS::AST::UiObjectDefinition *ast);
+ void readDependencies(QQmlJS::AST::UiScriptBinding *ast);
+ void readComponent(QQmlJS::AST::UiObjectDefinition *ast);
+ void readSignalOrMethod(QQmlJS::AST::UiObjectDefinition *ast, bool isMethod,
+ const ScopeTree::Ptr &scope);
+ void readProperty(QQmlJS::AST::UiObjectDefinition *ast, const ScopeTree::Ptr &scope);
+ void readEnum(QQmlJS::AST::UiObjectDefinition *ast, const ScopeTree::Ptr &scope);
+ void readParameter(QQmlJS::AST::UiObjectDefinition *ast, MetaMethod *metaMethod);
+
+ QString readStringBinding(QQmlJS::AST::UiScriptBinding *ast);
+ bool readBoolBinding(QQmlJS::AST::UiScriptBinding *ast);
+ double readNumericBinding(QQmlJS::AST::UiScriptBinding *ast);
+ ComponentVersion readNumericVersionBinding(QQmlJS::AST::UiScriptBinding *ast);
+ int readIntBinding(QQmlJS::AST::UiScriptBinding *ast);
+ void readExports(QQmlJS::AST::UiScriptBinding *ast, const ScopeTree::Ptr &scope);
+ void readMetaObjectRevisions(QQmlJS::AST::UiScriptBinding *ast, const ScopeTree::Ptr &scope);
+ void readEnumValues(QQmlJS::AST::UiScriptBinding *ast, MetaEnum *metaEnum);
+
+ void addError(const QQmlJS::SourceLocation &loc, const QString &message);
+ void addWarning(const QQmlJS::SourceLocation &loc, const QString &message);
+
+ QString m_fileName;
+ QString m_source;
+ QString m_errorMessage;
+ QString m_warningMessage;
+ QHash<QString, ScopeTree::ConstPtr> *m_objects = nullptr;
+ QStringList *m_dependencies = nullptr;
+};
+
+#endif // TYPEDESCRIPTIONREADER_H