aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2020-10-01 12:40:01 +0200
committerUlf Hermann <ulf.hermann@qt.io>2020-10-05 14:58:32 +0200
commit767dd738d3de9306062707fe05d32c91ed755da3 (patch)
treeebadcc9809322d5d78ce28f9b82bad1db949f232 /src
parente7d90fc5268cdca6aa10f422f00ad4a0049ea157 (diff)
Long live libQtQmlCompiler!
Move all the code from tools/shared into src/qmlcompiler and build a static library from it so that we can re-use it in external tools. Change-Id: I7c8d8e59063dc7c711f4072f103a01095e6f5997 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/qmlcompiler/CMakeLists.txt25
-rw-r--r--src/qmlcompiler/QtBootstrap/QtBootstrap1
-rw-r--r--src/qmlcompiler/QtQmlDevTools/QtQmlDevTools1
-rw-r--r--src/qmlcompiler/importedmembersvisitor.cpp170
-rw-r--r--src/qmlcompiler/importedmembersvisitor_p.h70
-rw-r--r--src/qmlcompiler/metatypes_p.h200
-rw-r--r--src/qmlcompiler/qmlcompiler.pro25
-rw-r--r--src/qmlcompiler/qmljsimporter.cpp291
-rw-r--r--src/qmlcompiler/qmljsimporter_p.h103
-rw-r--r--src/qmlcompiler/qmljstypereader.cpp136
-rw-r--r--src/qmlcompiler/qmljstypereader_p.h70
-rw-r--r--src/qmlcompiler/qmlstreamwriter.cpp190
-rw-r--r--src/qmlcompiler/qmlstreamwriter_p.h77
-rw-r--r--src/qmlcompiler/resourcefilemapper.cpp170
-rw-r--r--src/qmlcompiler/resourcefilemapper_p.h64
-rw-r--r--src/qmlcompiler/scopetree.cpp181
-rw-r--r--src/qmlcompiler/scopetree_p.h221
-rw-r--r--src/qmlcompiler/typedescriptionreader.cpp689
-rw-r--r--src/qmlcompiler/typedescriptionreader_p.h95
-rw-r--r--src/qmltyperegistrar/.prev_CMakeLists.txt4
-rw-r--r--src/qmltyperegistrar/CMakeLists.txt4
-rw-r--r--src/qmltyperegistrar/qmltyperegistrar.pro9
-rw-r--r--src/qmltyperegistrar/qmltypescreator.cpp1
-rw-r--r--src/qmltyperegistrar/qmltypescreator.h2
-rw-r--r--src/src.pro10
26 files changed, 2799 insertions, 11 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d3c26e73e6..bac92c5a38 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -38,6 +38,7 @@ add_subdirectory(plugins)
if(QT_FEATURE_qml_devtools)
add_subdirectory(qmldevtools)
+ add_subdirectory(qmlcompiler)
# Build qmlcachegen now, so that we can use it in src/imports.
if(QT_FEATURE_qml_devtools AND QT_FEATURE_xmlstreamwriter)
diff --git a/src/qmlcompiler/CMakeLists.txt b/src/qmlcompiler/CMakeLists.txt
new file mode 100644
index 0000000000..d6895704a4
--- /dev/null
+++ b/src/qmlcompiler/CMakeLists.txt
@@ -0,0 +1,25 @@
+# Generated from qmlcompiler.pro.
+
+#####################################################################
+## QmlCompiler Module:
+#####################################################################
+
+qt_add_module(QmlCompiler
+ STATIC
+ INTERNAL_MODULE
+ SOURCES
+ importedmembersvisitor.cpp importedmembersvisitor_p.h
+ metatypes_p.h
+ qmljsimporter.cpp qmljsimporter_p.h
+ qmljstypereader.cpp qmljstypereader_p.h
+ qmlstreamwriter.cpp qmlstreamwriter_p.h
+ resourcefilemapper.cpp resourcefilemapper_p.h
+ scopetree.cpp scopetree_p.h
+ typedescriptionreader.cpp typedescriptionreader_p.h
+ PUBLIC_LIBRARIES
+ Qt::CorePrivate
+ Qt::QmlDevToolsPrivate
+)
+
+#### Keys ignored in scope 1:.:.:qmlcompiler.pro:<TRUE>:
+# _OPTION = "host_build"
diff --git a/src/qmlcompiler/QtBootstrap/QtBootstrap b/src/qmlcompiler/QtBootstrap/QtBootstrap
new file mode 100644
index 0000000000..956abd4dbe
--- /dev/null
+++ b/src/qmlcompiler/QtBootstrap/QtBootstrap
@@ -0,0 +1 @@
+// We need this because qmake insists on including it and QtBootstrap doesn't ship it.
diff --git a/src/qmlcompiler/QtQmlDevTools/QtQmlDevTools b/src/qmlcompiler/QtQmlDevTools/QtQmlDevTools
new file mode 100644
index 0000000000..f4bcaed435
--- /dev/null
+++ b/src/qmlcompiler/QtQmlDevTools/QtQmlDevTools
@@ -0,0 +1 @@
+// We need this because qmake insists on including it and QtQmlDevTools doesn't ship it.
diff --git a/src/qmlcompiler/importedmembersvisitor.cpp b/src/qmlcompiler/importedmembersvisitor.cpp
new file mode 100644
index 0000000000..655b77d91f
--- /dev/null
+++ b/src/qmlcompiler/importedmembersvisitor.cpp
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** 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 "importedmembersvisitor_p.h"
+#include "scopetree_p.h"
+
+using namespace QQmlJS::AST;
+
+ScopeTree::Ptr ImportedMembersVisitor::result(const QString &scopeName) const
+{
+ ScopeTree::Ptr result = ScopeTree::create();
+ result->setIsComposite(true);
+ result->setInternalName(scopeName);
+ result->setBaseTypeName(m_rootObject->baseTypeName());
+ const auto properties = m_rootObject->properties();
+ for (auto property : properties) {
+ if (property.isAlias()) {
+ const auto it = m_objects.find(property.typeName());
+ if (it != m_objects.end())
+ property.setType(*it);
+ result->addProperty(property);
+ } else {
+ result->addProperty(property);
+ }
+ }
+
+ for (const auto &method : m_rootObject->methods())
+ result->addMethod(method);
+
+ for (const auto &enumerator : m_rootObject->enums())
+ result->addEnum(enumerator);
+
+ return result;
+}
+
+bool ImportedMembersVisitor::visit(UiObjectDefinition *definition)
+{
+ ScopeTree::Ptr scope = ScopeTree::create();
+ QString superType;
+ for (auto segment = definition->qualifiedTypeNameId; segment; segment = segment->next) {
+ if (!superType.isEmpty())
+ superType.append(u'.');
+ superType.append(segment->name.toString());
+ }
+ scope->setBaseTypeName(superType);
+ if (!m_rootObject)
+ m_rootObject = scope;
+ m_currentObjects.append(scope);
+ return true;
+}
+
+void ImportedMembersVisitor::endVisit(UiObjectDefinition *)
+{
+ m_currentObjects.pop_back();
+}
+
+bool ImportedMembersVisitor::visit(UiPublicMember *publicMember)
+{
+ switch (publicMember->type) {
+ case UiPublicMember::Signal: {
+ UiParameterList *param = publicMember->parameters;
+ MetaMethod method;
+ method.setMethodType(MetaMethod::Signal);
+ method.setMethodName(publicMember->name.toString());
+ while (param) {
+ method.addParameter(param->name.toString(), param->type->name.toString());
+ param = param->next;
+ }
+ currentObject()->addMethod(method);
+ break;
+ }
+ case UiPublicMember::Property: {
+ auto typeName = publicMember->memberType->name;
+ const bool isAlias = (typeName == QLatin1String("alias"));
+ if (isAlias) {
+ const auto expression = cast<ExpressionStatement *>(publicMember->statement);
+ if (const auto idExpression = cast<IdentifierExpression *>(expression->expression))
+ typeName = idExpression->name;
+ }
+ MetaProperty prop {
+ publicMember->name.toString(),
+ typeName.toString(),
+ false,
+ false,
+ false,
+ isAlias,
+ 0
+ };
+ currentObject()->addProperty(prop);
+ break;
+ }
+ }
+ return true;
+}
+
+bool ImportedMembersVisitor::visit(UiSourceElement *sourceElement)
+{
+ if (FunctionExpression *fexpr = sourceElement->sourceElement->asFunctionDefinition()) {
+ MetaMethod method;
+ method.setMethodName(fexpr->name.toString());
+ method.setMethodType(MetaMethod::Method);
+ FormalParameterList *parameters = fexpr->formals;
+ while (parameters) {
+ method.addParameter(parameters->element->bindingIdentifier.toString(), QString());
+ parameters = parameters->next;
+ }
+ currentObject()->addMethod(method);
+ } else if (ClassExpression *clexpr = sourceElement->sourceElement->asClassDefinition()) {
+ MetaProperty prop { clexpr->name.toString(), QString(), false, false, false, false, 1 };
+ currentObject()->addProperty(prop);
+ } else if (cast<VariableStatement *>(sourceElement->sourceElement)) {
+ // nothing to do
+ } else {
+ const auto loc = sourceElement->firstSourceLocation();
+ m_errors.append(
+ QStringLiteral("unsupportedd sourceElement at ")
+ + QString::fromLatin1("%1:%2: ").arg(loc.startLine).arg(loc.startColumn)
+ + QString::number(sourceElement->sourceElement->kind));
+ }
+ return true;
+}
+
+bool ImportedMembersVisitor::visit(UiScriptBinding *scriptBinding)
+{
+ if (scriptBinding->qualifiedId->name == QLatin1String("id")) {
+ const auto *statement = cast<ExpressionStatement *>(scriptBinding->statement);
+ const auto *idExprension = cast<IdentifierExpression *>(statement->expression);
+ m_objects.insert(idExprension->name.toString(), currentObject());
+ }
+ return true;
+}
+
+bool ImportedMembersVisitor::visit(QQmlJS::AST::UiEnumDeclaration *uied)
+{
+ MetaEnum qmlEnum(uied->name.toString());
+ for (const auto *member = uied->members; member; member = member->next)
+ qmlEnum.addKey(member->member.toString());
+ currentObject()->addEnum(qmlEnum);
+ return true;
+}
+
+void ImportedMembersVisitor::throwRecursionDepthError()
+{
+ m_errors.append(QStringLiteral("Maximum statement or expression depth exceeded"));
+}
diff --git a/src/qmlcompiler/importedmembersvisitor_p.h b/src/qmlcompiler/importedmembersvisitor_p.h
new file mode 100644
index 0000000000..b909265f0b
--- /dev/null
+++ b/src/qmlcompiler/importedmembersvisitor_p.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** 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 IMPORTEDMEMBERSVISITOR_H
+#define IMPORTEDMEMBERSVISITOR_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_p.h"
+
+#include <private/qqmljsast_p.h>
+
+class ImportedMembersVisitor : public QQmlJS::AST::Visitor
+{
+public:
+ ScopeTree::Ptr result(const QString &scopeName) const;
+ QStringList errors() const { return m_errors; }
+
+private:
+ bool visit(QQmlJS::AST::UiObjectDefinition *) override;
+ void endVisit(QQmlJS::AST::UiObjectDefinition *) override;
+ bool visit(QQmlJS::AST::UiPublicMember *) override;
+ bool visit(QQmlJS::AST::UiSourceElement *) override;
+ bool visit(QQmlJS::AST::UiScriptBinding *) override;
+ bool visit(QQmlJS::AST::UiEnumDeclaration *uied) override;
+ void throwRecursionDepthError() override;
+
+ ScopeTree::Ptr currentObject() const { return m_currentObjects.back(); }
+
+ QVector<ScopeTree::Ptr> m_currentObjects;
+ ScopeTree::ConstPtr m_rootObject;
+ QHash<QString, ScopeTree::Ptr> m_objects;
+
+ QStringList m_errors;
+};
+
+#endif // IMPORTEDMEMBERSVISITOR_H
diff --git a/src/qmlcompiler/metatypes_p.h b/src/qmlcompiler/metatypes_p.h
new file mode 100644
index 0000000000..d462fb684f
--- /dev/null
+++ b/src/qmlcompiler/metatypes_p.h
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** 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>
+#include <QtCore/qsharedpointer.h>
+
+// MetaMethod and MetaProperty have both type names and actual ScopeTree types.
+// When parsing the information from the relevant QML or qmltypes files, we only
+// see the names and don't have a complete picture of the types, yet. In a second
+// pass we typically fill in the types. The types may have multiple exported names
+// and the the name property of MetaProperty and MetaMethod still carries some
+// significance regarding which name was chosen to refer to the type. In a third
+// pass we may further specify the type if the context provides additional information.
+// The parent of an Item, for example, is typically not just a QtObject, but rather
+// some other Item with custom properties.
+
+class ScopeTree;
+class MetaEnum
+{
+ QStringList m_keys;
+ QString m_name;
+ QString m_alias;
+ bool m_isFlag = false;
+
+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; }
+
+ QString alias() const { return m_alias; }
+ void setAlias(const QString &alias) { m_alias = alias; }
+
+ bool isFlag() const { return m_isFlag; }
+ void setIsFlag(bool isFlag) { m_isFlag = isFlag; }
+
+ 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_returnTypeName(std::move(returnType))
+ , m_methodType(Method)
+ , m_methodAccess(Public)
+ {}
+
+ QString methodName() const { return m_name; }
+ void setMethodName(const QString &name) { m_name = name; }
+
+ QString returnTypeName() const { return m_returnTypeName; }
+ QSharedPointer<const ScopeTree> returnType() const { return m_returnType.toStrongRef(); }
+ void setReturnTypeName(const QString &type) { m_returnTypeName = type; }
+ void setReturnType(const QSharedPointer<const ScopeTree> &type)
+ {
+ m_returnType = type;
+ }
+
+ QStringList parameterNames() const { return m_paramNames; }
+ QStringList parameterTypeNames() const { return m_paramTypeNames; }
+ QList<QSharedPointer<const ScopeTree>> parameterTypes() const
+ {
+ QList<QSharedPointer<const ScopeTree>> result;
+ for (const auto &type : m_paramTypes)
+ result.append(type.toStrongRef());
+ return result;
+ }
+ void setParameterTypes(const QList<QSharedPointer<const ScopeTree>> &types)
+ {
+ Q_ASSERT(types.length() == m_paramNames.length());
+ m_paramTypes.clear();
+ for (const auto &type : types)
+ m_paramTypes.append(type);
+ }
+ void addParameter(const QString &name, const QString &typeName,
+ const QSharedPointer<const ScopeTree> &type = {})
+ {
+ m_paramNames.append(name);
+ m_paramTypeNames.append(typeName);
+ 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_returnTypeName;
+ QWeakPointer<const ScopeTree> m_returnType;
+
+ QStringList m_paramNames;
+ QStringList m_paramTypeNames;
+ QList<QWeakPointer<const ScopeTree>> m_paramTypes;
+
+ Type m_methodType = Signal;
+ Access m_methodAccess = Private;
+ int m_revision = 0;
+};
+
+class MetaProperty
+{
+ QString m_propertyName;
+ QString m_typeName;
+ QWeakPointer<const ScopeTree> m_type;
+ 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 QSharedPointer<const ScopeTree> &type) { m_type = type; }
+ QSharedPointer<const ScopeTree> type() const { return m_type.toStrongRef(); }
+
+ 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/src/qmlcompiler/qmlcompiler.pro b/src/qmlcompiler/qmlcompiler.pro
new file mode 100644
index 0000000000..906286551f
--- /dev/null
+++ b/src/qmlcompiler/qmlcompiler.pro
@@ -0,0 +1,25 @@
+option(host_build)
+TARGET = QtQmlCompiler
+QT = core-private qmldevtools-private
+CONFIG += internal_module
+
+SOURCES = \
+ resourcefilemapper.cpp \
+ importedmembersvisitor.cpp \
+ qmljsimporter.cpp \
+ qmljstypereader.cpp \
+ scopetree.cpp \
+ typedescriptionreader.cpp \
+ qmlstreamwriter.cpp
+
+HEADERS = \
+ resourcefilemapper_p.h \
+ importedmembersvisitor_p.h \
+ qmljsimporter_p.h \
+ qmljstypereader_p.h \
+ metatypes_p.h \
+ scopetree_p.h \
+ typedescriptionreader_p.h \
+ qmlstreamwriter_p.h
+
+load(qt_module)
diff --git a/src/qmlcompiler/qmljsimporter.cpp b/src/qmlcompiler/qmljsimporter.cpp
new file mode 100644
index 0000000000..a5701c72d4
--- /dev/null
+++ b/src/qmlcompiler/qmljsimporter.cpp
@@ -0,0 +1,291 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qmljsimporter_p.h"
+#include "typedescriptionreader_p.h"
+#include "qmljstypereader_p.h"
+
+#include <QtQml/private/qqmlimportresolver_p.h>
+
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdiriterator.h>
+
+static const QLatin1String SlashQmldir = QLatin1String("/qmldir");
+static const QLatin1String SlashPluginsDotQmltypes = QLatin1String("/plugins.qmltypes");
+
+static const QString prefixedName(const QString &prefix, const QString &name)
+{
+ Q_ASSERT(!prefix.endsWith(u'.'));
+ return prefix.isEmpty() ? name : (prefix + QLatin1Char('.') + name);
+}
+
+static QQmlDirParser createQmldirParserForFile(const QString &filename)
+{
+ QFile f(filename);
+ f.open(QFile::ReadOnly);
+ QQmlDirParser parser;
+ parser.parse(QString::fromUtf8(f.readAll()));
+ return parser;
+}
+
+void QmlJSImporter::readQmltypes(
+ const QString &filename, QHash<QString, ScopeTree::Ptr> *objects)
+{
+ const QFileInfo fileInfo(filename);
+ if (!fileInfo.exists()) {
+ m_warnings.append(QLatin1String("QML types file does not exist: ") + filename);
+ return;
+ }
+
+ if (fileInfo.isDir()) {
+ m_warnings.append(QLatin1String("QML types file cannot be a directory: ") + filename);
+ return;
+ }
+
+ QFile file(filename);
+ file.open(QFile::ReadOnly);
+ TypeDescriptionReader reader { filename, QString::fromUtf8(file.readAll()) };
+ QStringList dependencies;
+ auto succ = reader(objects, &dependencies);
+ if (!succ)
+ m_warnings.append(reader.errorMessage());
+}
+
+QmlJSImporter::Import QmlJSImporter::readQmldir(const QString &path)
+{
+ Import result;
+ auto reader = createQmldirParserForFile(path + SlashQmldir);
+ result.imports.append(reader.imports());
+ result.dependencies.append(reader.dependencies());
+
+ QHash<QString, ScopeTree::Ptr> qmlComponents;
+ const auto components = reader.components();
+ for (auto it = components.begin(), end = components.end(); it != end; ++it) {
+ const QString filePath = path + QLatin1Char('/') + it->fileName;
+ if (!QFile::exists(filePath)) {
+ m_warnings.append(it->fileName + QLatin1String(" is listed as component in ")
+ + path + SlashQmldir
+ + QLatin1String(" but does not exist.\n"));
+ continue;
+ }
+
+ auto mo = qmlComponents.find(it.key());
+ if (mo == qmlComponents.end())
+ mo = qmlComponents.insert(it.key(), localFile2ScopeTree(filePath));
+
+ (*mo)->addExport(it.key(), reader.typeNamespace(), it->version);
+ }
+ for (auto it = qmlComponents.begin(), end = qmlComponents.end(); it != end; ++it)
+ result.objects.insert(it.key(), it.value());
+
+ if (!reader.plugins().isEmpty() && QFile::exists(path + SlashPluginsDotQmltypes))
+ readQmltypes(path + SlashPluginsDotQmltypes, &result.objects);
+
+ const auto scripts = reader.scripts();
+ for (const auto &script : scripts) {
+ const QString filePath = path + QLatin1Char('/') + script.fileName;
+ result.scripts.insert(script.nameSpace, localFile2ScopeTree(filePath));
+ }
+ return result;
+}
+
+void QmlJSImporter::importDependencies(
+ const QmlJSImporter::Import &import,
+ QmlJSImporter::AvailableTypes *types, const QString &prefix, QTypeRevision version)
+{
+ // Import the dependencies with an invalid prefix. The prefix will never be matched by actual
+ // QML code but the C++ types will be visible.
+ const QString invalidPrefix = QString::fromLatin1("$dependency$");
+ for (auto const &dependency : qAsConst(import.dependencies))
+ importHelper(dependency.module, types, invalidPrefix, dependency.version);
+
+ for (auto const &import : qAsConst(import.imports)) {
+ importHelper(import.module, types, prefix,
+ import.isAutoImport ? version : import.version);
+ }
+}
+
+void QmlJSImporter::processImport(
+ const QmlJSImporter::Import &import,
+ QmlJSImporter::AvailableTypes *types,
+ const QString &prefix)
+{
+ for (auto it = import.scripts.begin(); it != import.scripts.end(); ++it)
+ types->qmlNames.insert(prefixedName(prefix, it.key()), it.value());
+
+ // add objects
+ for (auto it = import.objects.begin(); it != import.objects.end(); ++it) {
+ const auto &val = it.value();
+ types->cppNames.insert(val->internalName(), val);
+
+ const auto exports = val->exports();
+ for (const auto &valExport : exports)
+ types->qmlNames.insert(prefixedName(prefix, valExport.type()), val);
+ }
+
+ for (auto it = import.objects.begin(); it != import.objects.end(); ++it) {
+ const auto &val = it.value();
+ if (!val->isComposite()) // Otherwise we have already done it in localFile2ScopeTree()
+ val->resolveTypes(types->cppNames);
+ }
+}
+
+/*!
+ * Imports builtins.qmltypes found in any of the import paths.
+ */
+QmlJSImporter::ImportedTypes QmlJSImporter::importBuiltins()
+{
+ AvailableTypes types;
+
+ for (auto const &dir : m_importPaths) {
+ Import result;
+ QDirIterator it { dir, QStringList() << QLatin1String("builtins.qmltypes"), QDir::NoFilter,
+ QDirIterator::Subdirectories };
+ while (it.hasNext())
+ readQmltypes(it.next(), &result.objects);
+ importDependencies(result, &types);
+ processImport(result, &types);
+ }
+
+ return types.qmlNames;
+}
+
+/*!
+ * Imports types from the specified \a qmltypesFiles.
+ */
+QmlJSImporter::ImportedTypes QmlJSImporter::importQmltypes(const QStringList &qmltypesFiles)
+{
+ AvailableTypes types;
+ Import result;
+
+ for (const auto &qmltypeFile : qmltypesFiles)
+ readQmltypes(qmltypeFile, &result.objects);
+
+ importDependencies(result, &types);
+ processImport(result, &types);
+
+ return types.qmlNames;
+}
+
+QmlJSImporter::ImportedTypes QmlJSImporter::importModule(
+ const QString &module, const QString &prefix, QTypeRevision version)
+{
+ AvailableTypes result;
+ importHelper(module, &result, prefix, version);
+ return result.qmlNames;
+}
+
+void QmlJSImporter::importHelper(const QString &module, AvailableTypes *types,
+ const QString &prefix, QTypeRevision version)
+{
+
+ const QPair<QString, QTypeRevision> importId { module, version };
+ const auto it = m_seenImports.find(importId);
+ if (it != m_seenImports.end()) {
+ importDependencies(*it, types, prefix, version);
+ processImport(*it, types, prefix);
+ return;
+ }
+
+ const auto qmltypesPaths = qQmlResolveImportPaths(module, m_importPaths, version);
+ for (auto const &qmltypesPath : qmltypesPaths) {
+ const QFileInfo file(qmltypesPath + SlashQmldir);
+ if (file.exists()) {
+ const auto import = readQmldir(file.canonicalPath());
+ m_seenImports.insert(importId, import);
+ importDependencies(import, types, prefix, version);
+ processImport(import, types, prefix);
+ return;
+ }
+ }
+
+ m_seenImports.insert(importId, {});
+}
+
+ScopeTree::Ptr QmlJSImporter::localFile2ScopeTree(const QString &filePath)
+{
+ const auto seen = m_importedFiles.find(filePath);
+ if (seen != m_importedFiles.end())
+ return *seen;
+
+ QmlJSTypeReader typeReader(filePath);
+ ScopeTree::Ptr result = typeReader();
+ m_importedFiles.insert(filePath, result);
+
+ const QStringList errors = typeReader.errors();
+ for (const QString &error : errors)
+ m_warnings.append(error);
+
+ AvailableTypes types;
+
+ QDirIterator it {
+ QFileInfo(filePath).canonicalPath(),
+ QStringList() << QLatin1String("*.qml"),
+ QDir::NoFilter
+ };
+ while (it.hasNext()) {
+ ScopeTree::Ptr scope(localFile2ScopeTree(it.next()));
+ if (!scope->internalName().isEmpty())
+ types.qmlNames.insert(scope->internalName(), scope);
+ }
+
+ const auto imports = typeReader.imports();
+ for (const auto &import : imports)
+ importHelper(import.module, &types, import.prefix, import.version);
+
+ result->resolveTypes(types.qmlNames);
+ return result;
+}
+
+QmlJSImporter::ImportedTypes QmlJSImporter::importFileOrDirectory(
+ const QString &fileOrDirectory, const QString &prefix)
+{
+ AvailableTypes result;
+
+ QString name = fileOrDirectory;
+
+ QFileInfo fileInfo(name);
+ if (fileInfo.isFile()) {
+ ScopeTree::Ptr scope(localFile2ScopeTree(fileInfo.canonicalFilePath()));
+ result.qmlNames.insert(prefix.isEmpty() ? scope->internalName() : prefix, scope);
+ return result.qmlNames;
+ }
+
+ QDirIterator it {
+ fileInfo.canonicalFilePath(),
+ QStringList() << QLatin1String("*.qml"),
+ QDir::NoFilter
+ };
+ while (it.hasNext()) {
+ ScopeTree::Ptr scope(localFile2ScopeTree(it.next()));
+ if (!scope->internalName().isEmpty())
+ result.qmlNames.insert(prefixedName(prefix, scope->internalName()), scope);
+ }
+
+ return result.qmlNames;
+}
diff --git a/src/qmlcompiler/qmljsimporter_p.h b/src/qmlcompiler/qmljsimporter_p.h
new file mode 100644
index 0000000000..0fb81dac5f
--- /dev/null
+++ b/src/qmlcompiler/qmljsimporter_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** 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 QMLJSIMPORTER_H
+#define QMLJSIMPORTER_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_p.h"
+#include <QtQml/private/qqmldirparser_p.h>
+
+class QmlJSImporter
+{
+public:
+ using ImportedTypes = QHash<QString, ScopeTree::ConstPtr>;
+
+ QmlJSImporter(const QStringList &importPaths) : m_importPaths(importPaths) {}
+
+ ImportedTypes importBuiltins();
+ ImportedTypes importQmltypes(const QStringList &qmltypesFiles);
+ ImportedTypes importFileOrDirectory(
+ const QString &fileOrDirectory, const QString &prefix = QString());
+ ImportedTypes importModule(
+ const QString &module, const QString &prefix = QString(),
+ QTypeRevision version = QTypeRevision());
+
+ QStringList takeWarnings()
+ {
+ QStringList result = std::move(m_warnings);
+ m_warnings.clear();
+ return result;
+ }
+
+private:
+ struct AvailableTypes
+ {
+ // C++ names used in qmltypes files for non-composite types
+ QHash<QString, ScopeTree::ConstPtr> cppNames;
+
+ // Names the importing component sees, including any prefixes
+ QHash<QString, ScopeTree::ConstPtr> qmlNames;
+ };
+
+ struct Import {
+ QHash<QString, ScopeTree::Ptr> objects;
+ QHash<QString, ScopeTree::Ptr> scripts;
+ QList<QQmlDirParser::Import> imports;
+ QList<QQmlDirParser::Import> dependencies;
+ };
+
+ void importHelper(const QString &module, AvailableTypes *types,
+ const QString &prefix = QString(),
+ QTypeRevision version = QTypeRevision());
+ void processImport(const Import &import, AvailableTypes *types,
+ const QString &prefix = QString());
+ void importDependencies(const QmlJSImporter::Import &import,
+ AvailableTypes *types,
+ const QString &prefix = QString(),
+ QTypeRevision version = QTypeRevision());
+ void readQmltypes(const QString &filename, QHash<QString, ScopeTree::Ptr> *objects);
+ Import readQmldir(const QString &dirname);
+ ScopeTree::Ptr localFile2ScopeTree(const QString &filePath);
+
+ QStringList m_importPaths;
+ QHash<QPair<QString, QTypeRevision>, Import> m_seenImports;
+ QHash<QString, ScopeTree::Ptr> m_importedFiles;
+ QStringList m_warnings;
+};
+
+#endif // QMLJSIMPORTER_H
diff --git a/src/qmlcompiler/qmljstypereader.cpp b/src/qmlcompiler/qmljstypereader.cpp
new file mode 100644
index 0000000000..ef2f5c2cbb
--- /dev/null
+++ b/src/qmlcompiler/qmljstypereader.cpp
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qmljstypereader_p.h"
+#include "importedmembersvisitor_p.h"
+
+#include <QtQml/private/qqmljsast_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQml/private/qqmljsparser_p.h>
+#include <QtQml/private/qqmlimportresolver_p.h>
+
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdebug.h>
+
+static QList<QmlJSTypeReader::Import> parseHeaders(QQmlJS::AST::UiHeaderItemList *header)
+{
+ using namespace QQmlJS::AST;
+ QList<QmlJSTypeReader::Import> imports;
+
+ for (; header; header = header->next) {
+ auto import = cast<UiImport *>(header->headerItem);
+ if (!import)
+ continue;
+
+ QString path;
+ auto uri = import->importUri;
+ while (uri) {
+ path.append(uri->name);
+ path.append(u'.');
+ uri = uri->next;
+ }
+ path.chop(1);
+ imports.append({
+ path,
+ import->version ? import->version->version : QTypeRevision(),
+ import->asToken.isValid() ? import->importId.toString() : QString()
+ });
+ }
+
+ return imports;
+}
+
+static ScopeTree::Ptr parseProgram(QQmlJS::AST::Program *program, const QString &name)
+{
+ using namespace QQmlJS::AST;
+ ScopeTree::Ptr result = ScopeTree::create(ScopeType::JSLexicalScope);
+ result->setInternalName(name);
+ for (auto *statement = program->statements; statement; statement = statement->next) {
+ if (auto *function = cast<FunctionDeclaration *>(statement->statement)) {
+ MetaMethod method(function->name.toString());
+ method.setMethodType(MetaMethod::Method);
+ for (auto *parameters = function->formals; parameters; parameters = parameters->next)
+ method.addParameter(parameters->element->bindingIdentifier.toString(), QString());
+ result->addMethod(method);
+ }
+ }
+ return result;
+}
+
+ScopeTree::Ptr QmlJSTypeReader::operator()()
+{
+ using namespace QQmlJS::AST;
+ const QFileInfo info { m_file };
+ QString baseName = info.baseName();
+ const QString scopeName = baseName.endsWith(QStringLiteral(".ui")) ? baseName.chopped(3)
+ : baseName;
+
+ QQmlJS::Engine engine;
+ QQmlJS::Lexer lexer(&engine);
+
+ const QString lowerSuffix = info.suffix().toLower();
+ const bool isESModule = lowerSuffix == QLatin1String("mjs");
+ const bool isJavaScript = isESModule || lowerSuffix == QLatin1String("js");
+
+ QFile file(m_file);
+ if (!file.open(QFile::ReadOnly)) {
+ ScopeTree::Ptr result = ScopeTree::create(
+ isJavaScript ? ScopeType::JSLexicalScope : ScopeType::QMLScope);
+ result->setInternalName(scopeName);
+ return result;
+ }
+
+ QString code = QString::fromUtf8(file.readAll());
+ file.close();
+
+ lexer.setCode(code, /*line = */ 1, /*qmlMode=*/ !isJavaScript);
+ QQmlJS::Parser parser(&engine);
+
+ const bool success = isJavaScript ? (isESModule ? parser.parseModule()
+ : parser.parseProgram())
+ : parser.parse();
+ if (!success) {
+ ScopeTree::Ptr result = ScopeTree::create(
+ isJavaScript ? ScopeType::JSLexicalScope : ScopeType::QMLScope);
+ result->setInternalName(scopeName);
+ return result;
+ }
+
+ if (!isJavaScript) {
+ QQmlJS::AST::UiProgram *program = parser.ast();
+ m_imports = parseHeaders(program->headers);
+ ImportedMembersVisitor membersVisitor;
+ program->members->accept(&membersVisitor);
+ m_errors = membersVisitor.errors();
+ return membersVisitor.result(scopeName);
+ }
+
+ // TODO: Anything special to do with ES modules here?
+ return parseProgram(QQmlJS::AST::cast<QQmlJS::AST::Program *>(parser.rootNode()), scopeName);
+}
diff --git a/src/qmlcompiler/qmljstypereader_p.h b/src/qmlcompiler/qmljstypereader_p.h
new file mode 100644
index 0000000000..d3c361ff97
--- /dev/null
+++ b/src/qmlcompiler/qmljstypereader_p.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** 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 QMLJSTYPERADER_H
+#define QMLJSTYPERADER_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_p.h"
+
+#include <QtQml/private/qqmljsastfwd_p.h>
+
+#include <QtCore/qpair.h>
+#include <QtCore/qset.h>
+
+class QmlJSTypeReader
+{
+public:
+ struct Import {
+ QString module;
+ QTypeRevision version;
+ QString prefix;
+ };
+
+ QmlJSTypeReader(const QString &file) : m_file(file) {}
+
+ ScopeTree::Ptr operator()();
+ QList<Import> imports() const { return m_imports; }
+ QStringList errors() const { return m_errors; }
+
+private:
+ QString m_file;
+ QList<Import> m_imports;
+ QStringList m_errors;
+};
+
+#endif // QMLJSTYPEREADER_H
diff --git a/src/qmlcompiler/qmlstreamwriter.cpp b/src/qmlcompiler/qmlstreamwriter.cpp
new file mode 100644
index 0000000000..b5b9ee0d4a
--- /dev/null
+++ b/src/qmlcompiler/qmlstreamwriter.cpp
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "qmlstreamwriter_p.h"
+
+#include <QtCore/QBuffer>
+#include <QtCore/QStringList>
+
+QmlStreamWriter::QmlStreamWriter(QByteArray *array)
+ : m_indentDepth(0)
+ , m_pendingLineLength(0)
+ , m_maybeOneline(false)
+ , m_stream(new QBuffer(array))
+{
+ m_stream->open(QIODevice::WriteOnly);
+}
+
+void QmlStreamWriter::writeStartDocument()
+{
+}
+
+void QmlStreamWriter::writeEndDocument()
+{
+}
+
+void QmlStreamWriter::writeLibraryImport(const QString &uri, int majorVersion, int minorVersion, const QString &as)
+{
+ m_stream->write(QString::fromLatin1("import %1 %2.%3").arg(uri, QString::number(majorVersion), QString::number(minorVersion)).toUtf8());
+ if (!as.isEmpty())
+ m_stream->write(QString::fromLatin1(" as %1").arg(as).toUtf8());
+ m_stream->write("\n");
+}
+
+void QmlStreamWriter::writeStartObject(const QString &component)
+{
+ flushPotentialLinesWithNewlines();
+ writeIndent();
+ m_stream->write(QString::fromLatin1("%1 {").arg(component).toUtf8());
+ ++m_indentDepth;
+ m_maybeOneline = true;
+}
+
+void QmlStreamWriter::writeEndObject()
+{
+ if (m_maybeOneline && !m_pendingLines.isEmpty()) {
+ --m_indentDepth;
+ for (int i = 0; i < m_pendingLines.size(); ++i) {
+ m_stream->write(" ");
+ m_stream->write(m_pendingLines.at(i).trimmed());
+ if (i != m_pendingLines.size() - 1)
+ m_stream->write(";");
+ }
+ m_stream->write(" }\n");
+ m_pendingLines.clear();
+ m_pendingLineLength = 0;
+ m_maybeOneline = false;
+ } else {
+ flushPotentialLinesWithNewlines();
+ --m_indentDepth;
+ writeIndent();
+ m_stream->write("}\n");
+ }
+}
+
+void QmlStreamWriter::writeScriptBinding(const QString &name, const QString &rhs)
+{
+ writePotentialLine(QString::fromLatin1("%1: %2").arg(name, rhs).toUtf8());
+}
+
+void QmlStreamWriter::writeBooleanBinding(const QString &name, bool value)
+{
+ writeScriptBinding(name, value ? QLatin1String("true") : QLatin1String("false"));
+}
+
+void QmlStreamWriter::writeArrayBinding(const QString &name, const QStringList &elements)
+{
+ flushPotentialLinesWithNewlines();
+ writeIndent();
+
+ // try to use a single line
+ QString singleLine;
+ singleLine += QString::fromLatin1("%1: [").arg(name);
+ for (int i = 0; i < elements.size(); ++i) {
+ singleLine += elements.at(i);
+ if (i != elements.size() - 1)
+ singleLine += QLatin1String(", ");
+ }
+ singleLine += QLatin1String("]\n");
+ if (singleLine.size() + m_indentDepth * 4 < 80) {
+ m_stream->write(singleLine.toUtf8());
+ return;
+ }
+
+ // write multi-line
+ m_stream->write(QString::fromLatin1("%1: [\n").arg(name).toUtf8());
+ ++m_indentDepth;
+ for (int i = 0; i < elements.size(); ++i) {
+ writeIndent();
+ m_stream->write(elements.at(i).toUtf8());
+ if (i != elements.size() - 1) {
+ m_stream->write(",\n");
+ } else {
+ m_stream->write("\n");
+ }
+ }
+ --m_indentDepth;
+ writeIndent();
+ m_stream->write("]\n");
+}
+
+void QmlStreamWriter::write(const QString &data)
+{
+ flushPotentialLinesWithNewlines();
+ m_stream->write(data.toUtf8());
+}
+
+void QmlStreamWriter::writeScriptObjectLiteralBinding(const QString &name, const QList<QPair<QString, QString> > &keyValue)
+{
+ flushPotentialLinesWithNewlines();
+ writeIndent();
+ m_stream->write(QString::fromLatin1("%1: {\n").arg(name).toUtf8());
+ ++m_indentDepth;
+ for (int i = 0; i < keyValue.size(); ++i) {
+ const QString key = keyValue.at(i).first;
+ const QString value = keyValue.at(i).second;
+ writeIndent();
+ m_stream->write(QString::fromLatin1("%1: %2").arg(key, value).toUtf8());
+ if (i != keyValue.size() - 1) {
+ m_stream->write(",\n");
+ } else {
+ m_stream->write("\n");
+ }
+ }
+ --m_indentDepth;
+ writeIndent();
+ m_stream->write("}\n");
+}
+
+void QmlStreamWriter::writeIndent()
+{
+ m_stream->write(QByteArray(m_indentDepth * 4, ' '));
+}
+
+void QmlStreamWriter::writePotentialLine(const QByteArray &line)
+{
+ m_pendingLines.append(line);
+ m_pendingLineLength += line.size();
+ if (m_pendingLineLength >= 80) {
+ flushPotentialLinesWithNewlines();
+ }
+}
+
+void QmlStreamWriter::flushPotentialLinesWithNewlines()
+{
+ if (m_maybeOneline)
+ m_stream->write("\n");
+ for (const QByteArray &line : qAsConst(m_pendingLines)) {
+ writeIndent();
+ m_stream->write(line);
+ m_stream->write("\n");
+ }
+ m_pendingLines.clear();
+ m_pendingLineLength = 0;
+ m_maybeOneline = false;
+}
diff --git a/src/qmlcompiler/qmlstreamwriter_p.h b/src/qmlcompiler/qmlstreamwriter_p.h
new file mode 100644
index 0000000000..260923feed
--- /dev/null
+++ b/src/qmlcompiler/qmlstreamwriter_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 QMLSTREAMWRITER_H
+#define QMLSTREAMWRITER_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/QIODevice>
+#include <QtCore/QList>
+#include <QtCore/QString>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QPair>
+
+class QmlStreamWriter
+{
+public:
+ QmlStreamWriter(QByteArray *array);
+
+ void writeStartDocument();
+ void writeEndDocument();
+ void writeLibraryImport(const QString &uri, int majorVersion, int minorVersion, const QString &as = QString());
+ //void writeFilesystemImport(const QString &file, const QString &as = QString());
+ void writeStartObject(const QString &component);
+ void writeEndObject();
+ void writeScriptBinding(const QString &name, const QString &rhs);
+ void writeScriptObjectLiteralBinding(const QString &name, const QList<QPair<QString, QString> > &keyValue);
+ void writeArrayBinding(const QString &name, const QStringList &elements);
+ void write(const QString &data);
+ void writeBooleanBinding(const QString &name, bool value);
+
+private:
+ void writeIndent();
+ void writePotentialLine(const QByteArray &line);
+ void flushPotentialLinesWithNewlines();
+
+ int m_indentDepth;
+ QList<QByteArray> m_pendingLines;
+ int m_pendingLineLength;
+ bool m_maybeOneline;
+ QScopedPointer<QIODevice> m_stream;
+};
+
+#endif // QMLSTREAMWRITER_H
diff --git a/src/qmlcompiler/resourcefilemapper.cpp b/src/qmlcompiler/resourcefilemapper.cpp
new file mode 100644
index 0000000000..d97aa27695
--- /dev/null
+++ b/src/qmlcompiler/resourcefilemapper.cpp
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 "resourcefilemapper_p.h"
+
+#include <QFileInfo>
+#include <QDir>
+#include <QXmlStreamReader>
+
+ResourceFileMapper::ResourceFileMapper(const QStringList &resourceFiles)
+{
+ for (const QString &fileName: resourceFiles) {
+ QFile f(fileName);
+ if (!f.open(QIODevice::ReadOnly))
+ continue;
+ populateFromQrcFile(f);
+ }
+}
+
+bool ResourceFileMapper::isEmpty() const
+{
+ return qrcPathToFileSystemPath.isEmpty();
+}
+
+QStringList ResourceFileMapper::resourcePaths(const QString &fileName)
+{
+ const QString absPath = QDir::cleanPath(QDir::current().absoluteFilePath(fileName));
+ QStringList resourcePaths;
+ for (auto it = qrcPathToFileSystemPath.cbegin(), end = qrcPathToFileSystemPath.cend(); it != end; ++it) {
+ if (QFileInfo(it.value()) == QFileInfo(absPath))
+ resourcePaths.append(it.key());
+ }
+ return resourcePaths;
+}
+
+QStringList ResourceFileMapper::qmlCompilerFiles(FileOutput fo) const
+{
+ QStringList files;
+ for (auto it = qrcPathToFileSystemPath.constBegin(), end = qrcPathToFileSystemPath.constEnd();
+ it != end; ++it) {
+ const QString &qrcPath = it.key();
+ const QString suffix = QFileInfo(qrcPath).suffix();
+ if (suffix != QStringLiteral("qml") && suffix != QStringLiteral("js") && suffix != QStringLiteral("mjs"))
+ continue;
+ if (fo == FileOutput::AbsoluteFilePath)
+ files << it.value();
+ else
+ files << qrcPath;
+ }
+ return files;
+}
+
+void ResourceFileMapper::populateFromQrcFile(QFile &file)
+{
+ enum State {
+ InitialState,
+ InRCC,
+ InResource,
+ InFile
+ };
+ State state = InitialState;
+
+ QDir qrcDir = QFileInfo(file).absoluteDir();
+
+ QString prefix;
+ QString currentFileName;
+ QXmlStreamAttributes currentFileAttributes;
+
+ QXmlStreamReader reader(&file);
+ while (!reader.atEnd()) {
+ switch (reader.readNext()) {
+ case QXmlStreamReader::StartElement:
+ if (reader.name() == QStringLiteral("RCC")) {
+ if (state != InitialState)
+ return;
+ state = InRCC;
+ continue;
+ } else if (reader.name() == QStringLiteral("qresource")) {
+ if (state != InRCC)
+ return;
+ state = InResource;
+ QXmlStreamAttributes attributes = reader.attributes();
+ if (attributes.hasAttribute(QStringLiteral("prefix")))
+ prefix = attributes.value(QStringLiteral("prefix")).toString();
+ if (!prefix.startsWith(QLatin1Char('/')))
+ prefix.prepend(QLatin1Char('/'));
+ if (!prefix.endsWith(QLatin1Char('/')))
+ prefix.append(QLatin1Char('/'));
+ continue;
+ } else if (reader.name() == QStringLiteral("file")) {
+ if (state != InResource)
+ return;
+ state = InFile;
+ currentFileAttributes = reader.attributes();
+ continue;
+ }
+ return;
+
+ case QXmlStreamReader::EndElement:
+ if (reader.name() == QStringLiteral("file")) {
+ if (state != InFile)
+ return;
+ state = InResource;
+ continue;
+ } else if (reader.name() == QStringLiteral("qresource")) {
+ if (state != InResource)
+ return;
+ state = InRCC;
+ continue;
+ } else if (reader.name() == QStringLiteral("RCC")) {
+ if (state != InRCC)
+ return;
+ state = InitialState;
+ continue;
+ }
+ return;
+
+ case QXmlStreamReader::Characters: {
+ if (reader.isWhitespace())
+ break;
+ if (state != InFile)
+ return;
+ currentFileName = reader.text().toString();
+ if (currentFileName.isEmpty())
+ continue;
+
+ const QString fsPath = QDir::cleanPath(qrcDir.absoluteFilePath(currentFileName));
+
+ if (currentFileAttributes.hasAttribute(QStringLiteral("alias")))
+ currentFileName = currentFileAttributes.value(QStringLiteral("alias")).toString();
+
+ currentFileName = QDir::cleanPath(currentFileName);
+ while (currentFileName.startsWith(QLatin1String("../")))
+ currentFileName.remove(0, 3);
+
+ const QString qrcPath = prefix + currentFileName;
+ if (QFile::exists(fsPath))
+ qrcPathToFileSystemPath.insert(qrcPath, fsPath);
+ continue;
+ }
+
+ default: break;
+ }
+ }
+}
diff --git a/src/qmlcompiler/resourcefilemapper_p.h b/src/qmlcompiler/resourcefilemapper_p.h
new file mode 100644
index 0000000000..7fa35e4a6d
--- /dev/null
+++ b/src/qmlcompiler/resourcefilemapper_p.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE: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 RESOURCEFILEMAPPER_H
+#define RESOURCEFILEMAPPER_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 <QStringList>
+#include <QHash>
+#include <QFile>
+
+struct ResourceFileMapper
+{
+ enum class FileOutput {
+ RelativeFilePath,
+ AbsoluteFilePath
+ };
+ ResourceFileMapper(const QStringList &resourceFiles);
+
+ bool isEmpty() const;
+
+ QStringList resourcePaths(const QString &fileName);
+ QStringList qmlCompilerFiles(FileOutput fo = FileOutput::RelativeFilePath) const;
+
+private:
+ void populateFromQrcFile(QFile &file);
+
+ QHash<QString, QString> qrcPathToFileSystemPath;
+};
+
+#endif // RESOURCEFILEMAPPER_H
diff --git a/src/qmlcompiler/scopetree.cpp b/src/qmlcompiler/scopetree.cpp
new file mode 100644
index 0000000000..5e24aeca73
--- /dev/null
+++ b/src/qmlcompiler/scopetree.cpp
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include <QtCore/qqueue.h>
+#include <QtCore/qsharedpointer.h>
+
+#include <algorithm>
+
+ScopeTree::ScopeTree(ScopeType type, const ScopeTree::Ptr &parentScope)
+ : m_parentScope(parentScope), m_scopeType(type) {}
+
+ScopeTree::Ptr ScopeTree::create(ScopeType type, const ScopeTree::Ptr &parentScope)
+{
+ ScopeTree::Ptr childScope(new ScopeTree{type, parentScope});
+ if (parentScope) {
+ Q_ASSERT(type != ScopeType::QMLScope
+ || !parentScope->m_parentScope
+ || parentScope->parentScope()->m_scopeType == ScopeType::QMLScope
+ || parentScope->parentScope()->m_internalName == QLatin1String("global"));
+ parentScope->m_childScopes.push_back(childScope);
+ }
+ return childScope;
+}
+
+void ScopeTree::insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier)
+{
+ Q_ASSERT(m_scopeType != ScopeType::QMLScope);
+ if (identifier.kind == JavaScriptIdentifier::LexicalScoped
+ || identifier.kind == JavaScriptIdentifier::Injected
+ || m_scopeType == ScopeType::JSFunctionScope) {
+ m_jsIdentifiers.insert(name, identifier);
+ } else {
+ auto targetScope = parentScope();
+ while (targetScope->m_scopeType != ScopeType::JSFunctionScope)
+ targetScope = targetScope->parentScope();
+ targetScope->m_jsIdentifiers.insert(name, identifier);
+ }
+}
+
+void ScopeTree::insertPropertyIdentifier(const MetaProperty &property)
+{
+ addProperty(property);
+ MetaMethod method(property.propertyName() + QLatin1String("Changed"), QLatin1String("void"));
+ addMethod(method);
+}
+
+bool ScopeTree::isIdInCurrentScope(const QString &id) const
+{
+ return isIdInCurrentQMlScopes(id) || isIdInCurrentJSScopes(id);
+}
+
+bool ScopeTree::isIdInCurrentQMlScopes(const QString &id) const
+{
+ if (m_scopeType == ScopeType::QMLScope)
+ return m_properties.contains(id) || m_methods.contains(id) || m_enums.contains(id);
+
+ const auto qmlScope = findCurrentQMLScope(parentScope());
+ return qmlScope->m_properties.contains(id)
+ || qmlScope->m_methods.contains(id)
+ || qmlScope->m_enums.contains(id);
+}
+
+bool ScopeTree::isIdInCurrentJSScopes(const QString &id) const
+{
+ if (m_scopeType != ScopeType::QMLScope && m_jsIdentifiers.contains(id))
+ return true;
+
+ for (auto jsScope = parentScope(); jsScope; jsScope = jsScope->parentScope()) {
+ if (jsScope->m_scopeType != ScopeType::QMLScope && jsScope->m_jsIdentifiers.contains(id))
+ return true;
+ }
+
+ return false;
+}
+
+bool ScopeTree::isIdInjectedFromSignal(const QString &id) const
+{
+ const auto found = findJSIdentifier(id);
+ return found.has_value() && found->kind == JavaScriptIdentifier::Injected;
+}
+
+std::optional<JavaScriptIdentifier> ScopeTree::findJSIdentifier(const QString &id) const
+{
+ for (const auto *scope = this; scope; scope = scope->parentScope().data()) {
+ if (scope->m_scopeType == ScopeType::JSFunctionScope
+ || scope->m_scopeType == ScopeType::JSLexicalScope) {
+ auto it = scope->m_jsIdentifiers.find(id);
+ if (it != scope->m_jsIdentifiers.end())
+ return *it;
+ }
+ }
+
+ return std::optional<JavaScriptIdentifier>{};
+}
+
+void ScopeTree::resolveTypes(const QHash<QString, ScopeTree::ConstPtr> &contextualTypes)
+{
+ auto findType = [&](const QString &name) {
+ auto type = contextualTypes.constFind(name);
+ if (type != contextualTypes.constEnd())
+ return *type;
+
+ return ScopeTree::ConstPtr();
+ };
+
+ m_baseType = findType(m_baseTypeName);
+ m_attachedType = findType(m_attachedTypeName);
+
+ for (auto it = m_properties.begin(), end = m_properties.end(); it != end; ++it)
+ it->setType(findType(it->typeName()));
+
+ for (auto it = m_methods.begin(), end = m_methods.end(); it != end; ++it) {
+ it->setReturnType(findType(it->returnTypeName()));
+ const auto paramNames = it->parameterTypeNames();
+ QList<ScopeTree::ConstPtr> paramTypes;
+
+ for (const QString &paramName: paramNames)
+ paramTypes.append(findType(paramName));
+
+ it->setParameterTypes(paramTypes);
+ }
+}
+
+ScopeTree::ConstPtr ScopeTree::findCurrentQMLScope(const ScopeTree::ConstPtr &scope)
+{
+ auto qmlScope = scope;
+ while (qmlScope && qmlScope->m_scopeType != ScopeType::QMLScope)
+ qmlScope = qmlScope->parentScope();
+ return qmlScope;
+}
+
+void ScopeTree::addExport(const QString &name, const QString &package, const QTypeRevision &version)
+{
+ m_exports.append(Export(package, name, version, 0));
+}
+
+void ScopeTree::setExportMetaObjectRevision(int exportIndex, int metaObjectRevision)
+{
+ m_exports[exportIndex].setMetaObjectRevision(metaObjectRevision);
+}
+
+ScopeTree::Export::Export(QString package, QString type, const QTypeRevision &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/src/qmlcompiler/scopetree_p.h b/src/qmlcompiler/scopetree_p.h
new file mode 100644
index 0000000000..95fca89315
--- /dev/null
+++ b/src/qmlcompiler/scopetree_p.h
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** 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_p.h"
+
+#include <QtQml/private/qqmljssourcelocation_p.h>
+
+#include <QtCore/qset.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qversionnumber.h>
+
+#include <optional>
+
+enum class ScopeType
+{
+ JSFunctionScope,
+ JSLexicalScope,
+ QMLScope
+};
+
+struct JavaScriptIdentifier
+{
+ enum Kind {
+ Parameter,
+ FunctionScoped,
+ LexicalScoped,
+ Injected
+ };
+
+ Kind kind = FunctionScoped;
+ QQmlJS::SourceLocation location;
+};
+
+class ScopeTree
+{
+ Q_DISABLE_COPY_MOVE(ScopeTree)
+public:
+ using Ptr = QSharedPointer<ScopeTree>;
+ using WeakPtr = QWeakPointer<ScopeTree>;
+ using ConstPtr = QSharedPointer<const ScopeTree>;
+ using WeakConstPtr = QWeakPointer<const ScopeTree>;
+
+ enum class AccessSemantics {
+ Reference,
+ Value,
+ None
+ };
+
+ enum Flag {
+ Creatable = 0x1,
+ Composite = 0x2,
+ Singleton = 0x4
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+ Q_FLAGS(Flags);
+
+ class Export {
+ public:
+ Export() = default;
+ Export(QString package, QString type, const QTypeRevision &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;
+ QTypeRevision m_version;
+ int m_metaObjectRevision = 0;
+ };
+
+ static ScopeTree::Ptr create(ScopeType type = ScopeType::QMLScope,
+ const ScopeTree::Ptr &parentScope = ScopeTree::Ptr());
+ static ScopeTree::ConstPtr findCurrentQMLScope(const ScopeTree::ConstPtr &scope);
+
+ ScopeTree::Ptr parentScope() const { return m_parentScope.toStrongRef(); }
+
+ void insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier);
+
+ // inserts property as qml identifier as well as the corresponding
+ void insertPropertyIdentifier(const MetaProperty &prop);
+
+ bool isIdInCurrentScope(const QString &id) const;
+
+ ScopeType scopeType() const { return m_scopeType; }
+
+ void addMethods(const QMultiHash<QString, MetaMethod> &methods) { m_methods.unite(methods); }
+ void addMethod(const MetaMethod &method) { m_methods.insert(method.methodName(), method); }
+ QMultiHash<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 fileName() const { return m_fileName; }
+ void setFileName(const QString &file) { m_fileName = file; }
+
+ // The name the type uses to refer to itself. Either C++ class name or base name of
+ // QML file. isComposite tells us if this is a C++ or a QML name.
+ QString internalName() const { return m_internalName; }
+ void setInternalName(const QString &internalName) { m_internalName = internalName; }
+
+ void addExport(const QString &name, const QString &package, const QTypeRevision &version);
+ void setExportMetaObjectRevision(int exportIndex, int metaObjectRevision);
+ QList<Export> exports() const { return m_exports; }
+
+ // If isComposite(), this is the QML/JS name of the prototype. Otherwise it's the
+ // relevant base class (in the hierarchy starting from QObject) of a C++ type.
+ void setBaseTypeName(const QString &baseTypeName) { m_baseTypeName = baseTypeName; }
+ QString baseTypeName() const { return m_baseTypeName; }
+ ScopeTree::ConstPtr baseType() const { return m_baseType; }
+
+ void addProperty(const MetaProperty &prop) { m_properties.insert(prop.propertyName(), prop); }
+ QHash<QString, MetaProperty> properties() const { return m_properties; }
+
+ 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; }
+ ScopeTree::ConstPtr attachedType() const { return m_attachedType; }
+
+ bool isSingleton() const { return m_flags & Singleton; }
+ bool isCreatable() const { return m_flags & Creatable; }
+ bool isComposite() const { return m_flags & Composite; }
+ void setIsSingleton(bool v) { m_flags = v ? (m_flags | Singleton) : (m_flags & ~Singleton); }
+ void setIsCreatable(bool v) { m_flags = v ? (m_flags | Creatable) : (m_flags & ~Creatable); }
+ void setIsComposite(bool v) { m_flags = v ? (m_flags | Composite) : (m_flags & ~Composite); }
+
+ void setAccessSemantics(AccessSemantics semantics) { m_semantics = semantics; }
+ AccessSemantics accessSemantics() const { return m_semantics; }
+
+ bool isIdInCurrentQMlScopes(const QString &id) const;
+ bool isIdInCurrentJSScopes(const QString &id) const;
+ bool isIdInjectedFromSignal(const QString &id) const;
+
+ std::optional<JavaScriptIdentifier> findJSIdentifier(const QString &id) const;
+
+ QVector<ScopeTree::Ptr> childScopes() const
+ {
+ return m_childScopes;
+ }
+
+ void resolveTypes(const QHash<QString, ConstPtr> &contextualTypes);
+
+private:
+ ScopeTree(ScopeType type, const ScopeTree::Ptr &parentScope = ScopeTree::Ptr());
+
+ QHash<QString, JavaScriptIdentifier> m_jsIdentifiers;
+
+ QMultiHash<QString, MetaMethod> m_methods;
+ QHash<QString, MetaProperty> m_properties;
+ QHash<QString, MetaEnum> m_enums;
+
+ QVector<ScopeTree::Ptr> m_childScopes;
+ ScopeTree::WeakPtr m_parentScope;
+
+ QString m_fileName;
+ QString m_internalName;
+ QString m_baseTypeName;
+ ScopeTree::WeakConstPtr m_baseType;
+
+ ScopeType m_scopeType = ScopeType::QMLScope;
+ QList<Export> m_exports;
+
+ QString m_defaultPropertyName;
+ QString m_attachedTypeName;
+ ScopeTree::WeakConstPtr m_attachedType;
+
+ Flags m_flags;
+ AccessSemantics m_semantics = AccessSemantics::Reference;
+};
+
+#endif // SCOPETREE_H
diff --git a/src/qmlcompiler/typedescriptionreader.cpp b/src/qmlcompiler/typedescriptionreader.cpp
new file mode 100644
index 0000000000..30808e6e99
--- /dev/null
+++ b/src/qmlcompiler/typedescriptionreader.cpp
@@ -0,0 +1,689 @@
+/****************************************************************************
+**
+** 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_p.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::Ptr> *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 = ScopeTree::create();
+
+ 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("file")) {
+ scope->setFileName(readStringBinding(script));
+ } else if (name == QLatin1String("name")) {
+ scope->setInternalName(readStringBinding(script));
+ } else if (name == QLatin1String("prototype")) {
+ scope->setBaseTypeName(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 if (name == QLatin1String("accessSemantics")) {
+ const QString semantics = readStringBinding(script);
+ if (semantics == QLatin1String("reference")) {
+ scope->setAccessSemantics(ScopeTree::AccessSemantics::Reference);
+ } else if (semantics == QLatin1String("value")) {
+ scope->setAccessSemantics(ScopeTree::AccessSemantics::Value);
+ } else if (semantics == QLatin1String("none")) {
+ scope->setAccessSemantics(ScopeTree::AccessSemantics::None);
+ } else {
+ addWarning(script->firstSourceLocation(),
+ tr("Unknown access semantics \"%1\".").arg(semantics));
+ }
+ } 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->internalName().isEmpty()) {
+ addError(ast->firstSourceLocation(), tr("Component definition is missing a name binding."));
+ return;
+ }
+
+ m_objects->insert(scope->internalName(), 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.setReturnTypeName(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("alias")) {
+ metaEnum.setAlias(readStringBinding(script));
+ } else if (name == QLatin1String("isFlag")) {
+ metaEnum.setIsFlag(readBoolBinding(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;
+}
+
+static QTypeRevision parseVersion(const QString &versionString)
+{
+ const int dotIdx = versionString.indexOf(QLatin1Char('.'));
+ if (dotIdx == -1)
+ return QTypeRevision();
+ bool ok = false;
+ const int maybeMajor = QStringView{versionString}.left(dotIdx).toInt(&ok);
+ if (!ok)
+ return QTypeRevision();
+ const int maybeMinor = QStringView{versionString}.mid(dotIdx + 1).toInt(&ok);
+ if (!ok)
+ return QTypeRevision();
+ return QTypeRevision::fromVersion(maybeMajor, maybeMinor);
+}
+
+QTypeRevision TypeDescriptionReader::readNumericVersionBinding(UiScriptBinding *ast)
+{
+ QTypeRevision 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 parseVersion(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(' '));
+ const QTypeRevision version = parseVersion(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/src/qmlcompiler/typedescriptionreader_p.h b/src/qmlcompiler/typedescriptionreader_p.h
new file mode 100644
index 0000000000..ca79a68a4f
--- /dev/null
+++ b/src/qmlcompiler/typedescriptionreader_p.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_p.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::Ptr> *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);
+ QTypeRevision 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::Ptr> *m_objects = nullptr;
+ QStringList *m_dependencies = nullptr;
+};
+
+#endif // TYPEDESCRIPTIONREADER_H
diff --git a/src/qmltyperegistrar/.prev_CMakeLists.txt b/src/qmltyperegistrar/.prev_CMakeLists.txt
index 365aa30e40..7c2bfe5de7 100644
--- a/src/qmltyperegistrar/.prev_CMakeLists.txt
+++ b/src/qmltyperegistrar/.prev_CMakeLists.txt
@@ -8,7 +8,7 @@ qt_get_tool_target_name(target_name qmltyperegistrar)
qt_add_tool(${target_name}
TARGET_DESCRIPTION "QML Types Registrar"
SOURCES
- ../../tools/shared/qmlstreamwriter.cpp ../../tools/shared/qmlstreamwriter.h
+ ../qmlcompiler/qmlstreamwriter.cpp ../qmlcompiler/qmlstreamwriter_p.h
qmltyperegistrar.cpp
qmltypesclassdescription.cpp qmltypesclassdescription.h
qmltypescreator.cpp qmltypescreator.h
@@ -16,7 +16,7 @@ qt_add_tool(${target_name}
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
INCLUDE_DIRECTORIES
- ../../tools/shared
+ ../qmlcompiler
PUBLIC_LIBRARIES
Qt::CorePrivate
)
diff --git a/src/qmltyperegistrar/CMakeLists.txt b/src/qmltyperegistrar/CMakeLists.txt
index 27c7e0cadc..5f6568ac37 100644
--- a/src/qmltyperegistrar/CMakeLists.txt
+++ b/src/qmltyperegistrar/CMakeLists.txt
@@ -9,7 +9,7 @@ qt_add_tool(${target_name}
TARGET_DESCRIPTION "QML Types Registrar"
TOOLS_TARGET Qml # special case
SOURCES
- ../../tools/shared/qmlstreamwriter.cpp ../../tools/shared/qmlstreamwriter.h
+ ../qmlcompiler/qmlstreamwriter.cpp ../qmlcompiler/qmlstreamwriter_p.h
qmltyperegistrar.cpp
qmltypesclassdescription.cpp qmltypesclassdescription.h
qmltypescreator.cpp qmltypescreator.h
@@ -17,7 +17,7 @@ qt_add_tool(${target_name}
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII
INCLUDE_DIRECTORIES
- ../../tools/shared
+ ../qmlcompiler
PUBLIC_LIBRARIES
Qt::CorePrivate
)
diff --git a/src/qmltyperegistrar/qmltyperegistrar.pro b/src/qmltyperegistrar/qmltyperegistrar.pro
index 7ed3986dd7..eafaab6559 100644
--- a/src/qmltyperegistrar/qmltyperegistrar.pro
+++ b/src/qmltyperegistrar/qmltyperegistrar.pro
@@ -5,16 +5,19 @@ DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
QMAKE_TARGET_DESCRIPTION = QML Types Registrar
-include(../../tools/shared/shared.pri)
+# We cannot link against libQmlCompiler as qmltyperegistrar
+# has to be built before libQmlCompiler.
+
+INCLUDEPATH += $$PWD/../qmlcompiler
SOURCES += \
- $$QMLSTREAMWRITER_SOURCES \
+ ../qmlcompiler/qmlstreamwriter.cpp \
qmltyperegistrar.cpp \
qmltypesclassdescription.cpp \
qmltypescreator.cpp
HEADERS += \
- $$QMLSTREAMWRITER_HEADERS \
+ ../qmlcompiler/qmlstreamwriter_p.h \
qmltypesclassdescription.h \
qmltypescreator.h
diff --git a/src/qmltyperegistrar/qmltypescreator.cpp b/src/qmltyperegistrar/qmltypescreator.cpp
index 0c2a70c6d1..7e5fca0c53 100644
--- a/src/qmltyperegistrar/qmltypescreator.cpp
+++ b/src/qmltyperegistrar/qmltypescreator.cpp
@@ -27,7 +27,6 @@
****************************************************************************/
#include "qmltypescreator.h"
-#include "qmlstreamwriter.h"
#include "qmltypesclassdescription.h"
#include <QtCore/qset.h>
diff --git a/src/qmltyperegistrar/qmltypescreator.h b/src/qmltyperegistrar/qmltypescreator.h
index 53976e775e..9fc62f9a9e 100644
--- a/src/qmltyperegistrar/qmltypescreator.h
+++ b/src/qmltyperegistrar/qmltypescreator.h
@@ -29,8 +29,8 @@
#ifndef QMLTYPESCREATOR_H
#define QMLTYPESCREATOR_H
-#include "qmlstreamwriter.h"
#include "qmltypesclassdescription.h"
+#include "qmlstreamwriter_p.h"
#include <QtCore/qstring.h>
#include <QtCore/qset.h>
diff --git a/src/src.pro b/src/src.pro
index d48b6390bf..d4e66be413 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -38,9 +38,15 @@ SUBDIRS += \
plugins \
imports
-qtConfig(qml-devtools): SUBDIRS += qmldevtools
+qtConfig(qml-devtools) {
+ SUBDIRS += \
+ qmldevtools \
+ qmlcompiler
+
+ qmldevtools.depends = qml
+ qmlcompiler.depends = qmldevtools
+}
-qmldevtools.depends = qml
qtConfig(qml-network) {
QT_FOR_CONFIG += network