From 767dd738d3de9306062707fe05d32c91ed755da3 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 1 Oct 2020 12:40:01 +0200 Subject: 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 --- src/CMakeLists.txt | 1 + src/qmlcompiler/CMakeLists.txt | 25 + src/qmlcompiler/QtBootstrap/QtBootstrap | 1 + src/qmlcompiler/QtQmlDevTools/QtQmlDevTools | 1 + src/qmlcompiler/importedmembersvisitor.cpp | 170 +++++++ src/qmlcompiler/importedmembersvisitor_p.h | 70 +++ src/qmlcompiler/metatypes_p.h | 200 ++++++++ src/qmlcompiler/qmlcompiler.pro | 25 + src/qmlcompiler/qmljsimporter.cpp | 291 ++++++++++++ src/qmlcompiler/qmljsimporter_p.h | 103 +++++ src/qmlcompiler/qmljstypereader.cpp | 136 ++++++ src/qmlcompiler/qmljstypereader_p.h | 70 +++ src/qmlcompiler/qmlstreamwriter.cpp | 190 ++++++++ src/qmlcompiler/qmlstreamwriter_p.h | 77 ++++ src/qmlcompiler/resourcefilemapper.cpp | 170 +++++++ src/qmlcompiler/resourcefilemapper_p.h | 64 +++ src/qmlcompiler/scopetree.cpp | 181 ++++++++ src/qmlcompiler/scopetree_p.h | 221 +++++++++ src/qmlcompiler/typedescriptionreader.cpp | 689 ++++++++++++++++++++++++++++ src/qmlcompiler/typedescriptionreader_p.h | 95 ++++ src/qmltyperegistrar/.prev_CMakeLists.txt | 4 +- src/qmltyperegistrar/CMakeLists.txt | 4 +- src/qmltyperegistrar/qmltyperegistrar.pro | 9 +- src/qmltyperegistrar/qmltypescreator.cpp | 1 - src/qmltyperegistrar/qmltypescreator.h | 2 +- src/src.pro | 10 +- sync.profile | 1 + tools/qmlcachegen/.prev_CMakeLists.txt | 4 +- tools/qmlcachegen/CMakeLists.txt | 4 +- tools/qmlcachegen/qmlcachegen.cpp | 3 +- tools/qmlcachegen/qmlcachegen.pro | 8 +- tools/qmlimportscanner/.prev_CMakeLists.txt | 4 +- tools/qmlimportscanner/CMakeLists.txt | 4 +- tools/qmlimportscanner/main.cpp | 3 +- tools/qmlimportscanner/qmlimportscanner.pro | 8 +- tools/qmllint/.prev_CMakeLists.txt | 9 +- tools/qmllint/CMakeLists.txt | 9 +- tools/qmllint/checkidentifiers.h | 4 +- tools/qmllint/findwarnings.cpp | 9 +- tools/qmllint/findwarnings.h | 7 +- tools/qmllint/qmllint.pro | 6 +- tools/qmlplugindump/.prev_CMakeLists.txt | 4 +- tools/qmlplugindump/CMakeLists.txt | 4 +- tools/qmlplugindump/main.cpp | 2 +- tools/qmlplugindump/qmlplugindump.pro | 9 +- tools/shared/importedmembersvisitor.cpp | 170 ------- tools/shared/importedmembersvisitor.h | 71 --- tools/shared/metatypes.h | 200 -------- tools/shared/qmljsimporter.cpp | 291 ------------ tools/shared/qmljsimporter.h | 103 ----- tools/shared/qmljstypereader.cpp | 136 ------ tools/shared/qmljstypereader.h | 70 --- tools/shared/qmlstreamwriter.cpp | 190 -------- tools/shared/qmlstreamwriter.h | 67 --- tools/shared/resourcefilemapper.cpp | 170 ------- tools/shared/resourcefilemapper.h | 54 --- tools/shared/scopetree.cpp | 181 -------- tools/shared/scopetree.h | 221 --------- tools/shared/shared.pri | 31 -- tools/shared/typedescriptionreader.cpp | 689 ---------------------------- tools/shared/typedescriptionreader.h | 95 ---- 61 files changed, 2833 insertions(+), 2818 deletions(-) create mode 100644 src/qmlcompiler/CMakeLists.txt create mode 100644 src/qmlcompiler/QtBootstrap/QtBootstrap create mode 100644 src/qmlcompiler/QtQmlDevTools/QtQmlDevTools create mode 100644 src/qmlcompiler/importedmembersvisitor.cpp create mode 100644 src/qmlcompiler/importedmembersvisitor_p.h create mode 100644 src/qmlcompiler/metatypes_p.h create mode 100644 src/qmlcompiler/qmlcompiler.pro create mode 100644 src/qmlcompiler/qmljsimporter.cpp create mode 100644 src/qmlcompiler/qmljsimporter_p.h create mode 100644 src/qmlcompiler/qmljstypereader.cpp create mode 100644 src/qmlcompiler/qmljstypereader_p.h create mode 100644 src/qmlcompiler/qmlstreamwriter.cpp create mode 100644 src/qmlcompiler/qmlstreamwriter_p.h create mode 100644 src/qmlcompiler/resourcefilemapper.cpp create mode 100644 src/qmlcompiler/resourcefilemapper_p.h create mode 100644 src/qmlcompiler/scopetree.cpp create mode 100644 src/qmlcompiler/scopetree_p.h create mode 100644 src/qmlcompiler/typedescriptionreader.cpp create mode 100644 src/qmlcompiler/typedescriptionreader_p.h delete mode 100644 tools/shared/importedmembersvisitor.cpp delete mode 100644 tools/shared/importedmembersvisitor.h delete mode 100644 tools/shared/metatypes.h delete mode 100644 tools/shared/qmljsimporter.cpp delete mode 100644 tools/shared/qmljsimporter.h delete mode 100644 tools/shared/qmljstypereader.cpp delete mode 100644 tools/shared/qmljstypereader.h delete mode 100644 tools/shared/qmlstreamwriter.cpp delete mode 100644 tools/shared/qmlstreamwriter.h delete mode 100644 tools/shared/resourcefilemapper.cpp delete mode 100644 tools/shared/resourcefilemapper.h delete mode 100644 tools/shared/scopetree.cpp delete mode 100644 tools/shared/scopetree.h delete mode 100644 tools/shared/shared.pri delete mode 100644 tools/shared/typedescriptionreader.cpp delete mode 100644 tools/shared/typedescriptionreader.h 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:: +# _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(publicMember->statement); + if (const auto idExpression = cast(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(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(scriptBinding->statement); + const auto *idExprension = cast(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 + +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 m_currentObjects; + ScopeTree::ConstPtr m_rootObject; + QHash 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 +#include +#include + +// 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 returnType() const { return m_returnType.toStrongRef(); } + void setReturnTypeName(const QString &type) { m_returnTypeName = type; } + void setReturnType(const QSharedPointer &type) + { + m_returnType = type; + } + + QStringList parameterNames() const { return m_paramNames; } + QStringList parameterTypeNames() const { return m_paramTypeNames; } + QList> parameterTypes() const + { + QList> result; + for (const auto &type : m_paramTypes) + result.append(type.toStrongRef()); + return result; + } + void setParameterTypes(const QList> &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 &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 m_returnType; + + QStringList m_paramNames; + QStringList m_paramTypeNames; + QList> m_paramTypes; + + Type m_methodType = Signal; + Access m_methodAccess = Private; + int m_revision = 0; +}; + +class MetaProperty +{ + QString m_propertyName; + QString m_typeName; + QWeakPointer 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 &type) { m_type = type; } + QSharedPointer 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 + +#include +#include + +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 *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 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 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 + +class QmlJSImporter +{ +public: + using ImportedTypes = QHash; + + 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 cppNames; + + // Names the importing component sees, including any prefixes + QHash qmlNames; + }; + + struct Import { + QHash objects; + QHash scripts; + QList imports; + QList 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 *objects); + Import readQmldir(const QString &dirname); + ScopeTree::Ptr localFile2ScopeTree(const QString &filePath); + + QStringList m_importPaths; + QHash, Import> m_seenImports; + QHash 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 +#include +#include +#include +#include + +#include +#include + +static QList parseHeaders(QQmlJS::AST::UiHeaderItemList *header) +{ + using namespace QQmlJS::AST; + QList imports; + + for (; header; header = header->next) { + auto import = cast(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(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(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 + +#include +#include + +class QmlJSTypeReader +{ +public: + struct Import { + QString module; + QTypeRevision version; + QString prefix; + }; + + QmlJSTypeReader(const QString &file) : m_file(file) {} + + ScopeTree::Ptr operator()(); + QList imports() const { return m_imports; } + QStringList errors() const { return m_errors; } + +private: + QString m_file; + QList 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 +#include + +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 > &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 +#include +#include +#include +#include + +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 > &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 m_pendingLines; + int m_pendingLineLength; + bool m_maybeOneline; + QScopedPointer 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 +#include +#include + +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 +#include +#include + +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 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 +#include + +#include + +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 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{}; +} + +void ScopeTree::resolveTypes(const QHash &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 paramTypes; + + for (const QString ¶mName: 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 + +#include +#include +#include +#include + +#include + +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; + using WeakPtr = QWeakPointer; + using ConstPtr = QSharedPointer; + using WeakConstPtr = QWeakPointer; + + 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 &methods) { m_methods.unite(methods); } + void addMethod(const MetaMethod &method) { m_methods.insert(method.methodName(), method); } + QMultiHash methods() const { return m_methods; } + + void addEnum(const MetaEnum &fakeEnum) { m_enums.insert(fakeEnum.name(), fakeEnum); } + QHash 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 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 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 findJSIdentifier(const QString &id) const; + + QVector childScopes() const + { + return m_childScopes; + } + + void resolveTypes(const QHash &contextualTypes); + +private: + ScopeTree(ScopeType type, const ScopeTree::Ptr &parentScope = ScopeTree::Ptr()); + + QHash m_jsIdentifiers; + + QMultiHash m_methods; + QHash m_properties; + QHash m_enums; + + QVector 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 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 +#include +#include + +#include + +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 *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(ast->headers->headerItem)) { + addError(SourceLocation(), tr("Expected a single import.")); + return; + } + + auto *import = cast(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(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(member); + + auto *script = cast(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(ast->statement); + if (!stmt) { + addError(ast->statement->firstSourceLocation(), tr("Expected dependency definitions")); + return; + } + auto *exp = cast(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(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(member); + auto *script = cast(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(member); + auto *script = cast(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(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(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(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(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), tr("Expected string after colon.")); + return QString(); + } + + auto *stringLit = cast(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(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon.")); + return false; + } + + auto *trueLit = cast(expStmt->expression); + auto *falseLit = cast(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(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), + tr("Expected numeric literal after colon.")); + return 0; + } + + auto *numericLit = cast(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(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), + tr("Expected numeric literal after colon.")); + return invalidVersion; + } + + auto *numericLit = cast(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(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(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), + tr("Expected array of strings after colon.")); + return; + } + + auto *arrayLit = cast(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(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(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), + tr("Expected array of numbers after colon.")); + return; + } + + auto *arrayLit = cast(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(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(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(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon.")); + return; + } + + if (auto *objectLit = cast(expStmt->expression)) { + for (PatternPropertyList *it = objectLit->properties; it; it = it->next) { + if (PatternProperty *assignement = it->property) { + if (auto *name = cast(assignement->name)) { + metaEnum->addKey(name->id.toString()); + continue; + } + } + addError(it->firstSourceLocation(), tr("Expected strings as enum keys.")); + } + } else if (auto *arrayLit = cast(expStmt->expression)) { + for (PatternElementList *it = arrayLit->elements; it; it = it->next) { + if (PatternElement *element = it->element) { + if (auto *name = cast(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 + +// for Q_DECLARE_TR_FUNCTIONS +#include + +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 *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 *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 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 #include 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 diff --git a/sync.profile b/sync.profile index 06cff0f960..eac24770f1 100644 --- a/sync.profile +++ b/sync.profile @@ -9,6 +9,7 @@ "QtQmlDebug" => "$basedir/src/qmldebug", "QtQmlModels" => "$basedir/src/qmlmodels", "QtQmlWorkerScript" => "$basedir/src/qmlworkerscript", + "QtQmlCompiler" => "$basedir/src/qmlcompiler", ); %inject_headers = ( "$basedir/src/qml" => [ "^qqmljsgrammar_p.h", "^qqmljsparser_p.h", "^qml_compile_hash_p.h" ], diff --git a/tools/qmlcachegen/.prev_CMakeLists.txt b/tools/qmlcachegen/.prev_CMakeLists.txt index d21161d202..06b03d8e96 100644 --- a/tools/qmlcachegen/.prev_CMakeLists.txt +++ b/tools/qmlcachegen/.prev_CMakeLists.txt @@ -8,16 +8,14 @@ qt_get_tool_target_name(target_name qmlcachegen) qt_add_tool(${target_name} TARGET_DESCRIPTION "QML Cache Generator" SOURCES - ../shared/resourcefilemapper.cpp ../shared/resourcefilemapper.h generateloader.cpp qmlcachegen.cpp resourcefilter.cpp DEFINES QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII - INCLUDE_DIRECTORIES - ../shared PUBLIC_LIBRARIES + Qt::QmlCompilerPrivate Qt::QmlDevToolsPrivate ) diff --git a/tools/qmlcachegen/CMakeLists.txt b/tools/qmlcachegen/CMakeLists.txt index 8cbe8dcfac..a509166dee 100644 --- a/tools/qmlcachegen/CMakeLists.txt +++ b/tools/qmlcachegen/CMakeLists.txt @@ -9,16 +9,14 @@ qt_add_tool(${target_name} TARGET_DESCRIPTION "QML Cache Generator" TOOLS_TARGET Qml # special case SOURCES - ../shared/resourcefilemapper.cpp ../shared/resourcefilemapper.h generateloader.cpp qmlcachegen.cpp resourcefilter.cpp DEFINES QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII - INCLUDE_DIRECTORIES - ../shared PUBLIC_LIBRARIES + Qt::QmlCompilerPrivate Qt::QmlDevToolsPrivate ) diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index 174cd547f6..d8466a2a2d 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -40,8 +40,7 @@ #include #include #include - -#include "resourcefilemapper.h" +#include #include diff --git a/tools/qmlcachegen/qmlcachegen.pro b/tools/qmlcachegen/qmlcachegen.pro index d02746cff7..530a0d54a1 100644 --- a/tools/qmlcachegen/qmlcachegen.pro +++ b/tools/qmlcachegen/qmlcachegen.pro @@ -1,19 +1,13 @@ option(host_build) -QT = qmldevtools-private +QT = qmldevtools-private qmlcompiler-private DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII -include(../shared/shared.pri) - SOURCES = \ - $$RESOURCEFILEMAPPER_SOURCES \ qmlcachegen.cpp \ resourcefilter.cpp \ generateloader.cpp -HEADERS = \ - $$RESOURCEFILEMAPPER_HEADERS - TARGET = qmlcachegen build_integration.files = qmlcache.prf qtquickcompiler.prf diff --git a/tools/qmlimportscanner/.prev_CMakeLists.txt b/tools/qmlimportscanner/.prev_CMakeLists.txt index b50a92e874..4be5ad8f06 100644 --- a/tools/qmlimportscanner/.prev_CMakeLists.txt +++ b/tools/qmlimportscanner/.prev_CMakeLists.txt @@ -8,14 +8,12 @@ qt_get_tool_target_name(target_name qmlimportscanner) qt_add_tool(${target_name} TARGET_DESCRIPTION "QML Import Scanner" SOURCES - ../shared/resourcefilemapper.cpp ../shared/resourcefilemapper.h main.cpp DEFINES QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII - INCLUDE_DIRECTORIES - ../shared PUBLIC_LIBRARIES + Qt::QmlCompilerPrivate Qt::QmlDevToolsPrivate ) diff --git a/tools/qmlimportscanner/CMakeLists.txt b/tools/qmlimportscanner/CMakeLists.txt index 3db7579258..8bb3dd9908 100644 --- a/tools/qmlimportscanner/CMakeLists.txt +++ b/tools/qmlimportscanner/CMakeLists.txt @@ -9,14 +9,12 @@ qt_add_tool(${target_name} TARGET_DESCRIPTION "QML Import Scanner" TOOLS_TARGET Qml # special case SOURCES - ../shared/resourcefilemapper.cpp ../shared/resourcefilemapper.h main.cpp DEFINES QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII - INCLUDE_DIRECTORIES - ../shared PUBLIC_LIBRARIES + Qt::QmlCompilerPrivate Qt::QmlDevToolsPrivate ) diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index f296cc7101..00ad0f0701 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -50,8 +51,6 @@ #include #include -#include - #include #include diff --git a/tools/qmlimportscanner/qmlimportscanner.pro b/tools/qmlimportscanner/qmlimportscanner.pro index 0d2dc22ea2..67f28737e0 100644 --- a/tools/qmlimportscanner/qmlimportscanner.pro +++ b/tools/qmlimportscanner/qmlimportscanner.pro @@ -1,17 +1,11 @@ option(host_build) -QT = core qmldevtools-private +QT = core qmldevtools-private qmlcompiler-private DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII -include(../shared/shared.pri) - SOURCES += \ - $$RESOURCEFILEMAPPER_SOURCES \ main.cpp -HEADERS += \ - $$RESOURCEFILEMAPPER_HEADERS - load(cmake_functions) CMAKE_BIN_DIR = $$cmakeRelativePath($$[QT_HOST_BINS], $$[QT_INSTALL_PREFIX]) diff --git a/tools/qmllint/.prev_CMakeLists.txt b/tools/qmllint/.prev_CMakeLists.txt index 3034d8699f..923371c587 100644 --- a/tools/qmllint/.prev_CMakeLists.txt +++ b/tools/qmllint/.prev_CMakeLists.txt @@ -8,20 +8,13 @@ qt_get_tool_target_name(target_name qmllint) qt_add_tool(${target_name} TARGET_DESCRIPTION "QML Syntax Verifier" SOURCES - ../shared/importedmembersvisitor.cpp ../shared/importedmembersvisitor.h - ../shared/metatypes.h - ../shared/qmljsimporter.cpp ../shared/qmljsimporter.h - ../shared/qmljstypereader.cpp ../shared/qmljstypereader.h - ../shared/scopetree.cpp ../shared/scopetree.h - ../shared/typedescriptionreader.cpp ../shared/typedescriptionreader.h checkidentifiers.cpp checkidentifiers.h findwarnings.cpp findwarnings.h main.cpp qcoloroutput.cpp qcoloroutput.h - INCLUDE_DIRECTORIES - ../shared PUBLIC_LIBRARIES Qt::CorePrivate + Qt::QmlCompilerPrivate Qt::QmlDevToolsPrivate ) diff --git a/tools/qmllint/CMakeLists.txt b/tools/qmllint/CMakeLists.txt index e9d92cc134..9230d8117b 100644 --- a/tools/qmllint/CMakeLists.txt +++ b/tools/qmllint/CMakeLists.txt @@ -9,20 +9,13 @@ qt_add_tool(${target_name} TARGET_DESCRIPTION "QML Syntax Verifier" TOOLS_TARGET Qml # special case SOURCES - ../shared/importedmembersvisitor.cpp ../shared/importedmembersvisitor.h - ../shared/metatypes.h - ../shared/qmljsimporter.cpp ../shared/qmljsimporter.h - ../shared/qmljstypereader.cpp ../shared/qmljstypereader.h - ../shared/scopetree.cpp ../shared/scopetree.h - ../shared/typedescriptionreader.cpp ../shared/typedescriptionreader.h checkidentifiers.cpp checkidentifiers.h findwarnings.cpp findwarnings.h main.cpp qcoloroutput.cpp qcoloroutput.h - INCLUDE_DIRECTORIES - ../shared PUBLIC_LIBRARIES Qt::CorePrivate + Qt::QmlCompilerPrivate Qt::QmlDevToolsPrivate ) diff --git a/tools/qmllint/checkidentifiers.h b/tools/qmllint/checkidentifiers.h index f05a227be7..181b54d290 100644 --- a/tools/qmllint/checkidentifiers.h +++ b/tools/qmllint/checkidentifiers.h @@ -29,8 +29,8 @@ #ifndef CHECKIDENTIFIERS_H #define CHECKIDENTIFIERS_H -#include "scopetree.h" -#include "qmljsimporter.h" +#include +#include class ColorOutput; diff --git a/tools/qmllint/findwarnings.cpp b/tools/qmllint/findwarnings.cpp index e2a5834109..128e931277 100644 --- a/tools/qmllint/findwarnings.cpp +++ b/tools/qmllint/findwarnings.cpp @@ -27,11 +27,12 @@ ****************************************************************************/ #include "findwarnings.h" -#include "importedmembersvisitor.h" -#include "scopetree.h" -#include "typedescriptionreader.h" #include "checkidentifiers.h" -#include "qmljstypereader.h" + +#include +#include +#include +#include #include #include diff --git a/tools/qmllint/findwarnings.h b/tools/qmllint/findwarnings.h index 95d4055cbe..2f5007b124 100644 --- a/tools/qmllint/findwarnings.h +++ b/tools/qmllint/findwarnings.h @@ -39,12 +39,13 @@ // // We mean it. -#include "typedescriptionreader.h" -#include "scopetree.h" #include "qcoloroutput.h" -#include "qmljsimporter.h" #include "checkidentifiers.h" +#include +#include +#include + #include #include #include diff --git a/tools/qmllint/qmllint.pro b/tools/qmllint/qmllint.pro index 44bf2e7ce5..1007ec5060 100644 --- a/tools/qmllint/qmllint.pro +++ b/tools/qmllint/qmllint.pro @@ -1,11 +1,8 @@ option(host_build) -QT = core-private qmldevtools-private - -include(../shared/shared.pri) +QT = core-private qmldevtools-private qmlcompiler-private SOURCES += \ - $$METATYPEREADER_SOURCES \ checkidentifiers.cpp \ main.cpp \ findwarnings.cpp \ @@ -16,7 +13,6 @@ QMAKE_TARGET_DESCRIPTION = QML Syntax Verifier load(qt_tool) HEADERS += \ - $$METATYPEREADER_HEADERS \ checkidentifiers.h \ findwarnings.h \ qcoloroutput.h diff --git a/tools/qmlplugindump/.prev_CMakeLists.txt b/tools/qmlplugindump/.prev_CMakeLists.txt index 79a4b0b18d..bf65d71c49 100644 --- a/tools/qmlplugindump/.prev_CMakeLists.txt +++ b/tools/qmlplugindump/.prev_CMakeLists.txt @@ -8,11 +8,11 @@ qt_get_tool_target_name(target_name qmlplugindump) qt_add_tool(${target_name} TARGET_DESCRIPTION "QML Plugin Metadata Dumper" SOURCES - ../shared/qmlstreamwriter.cpp ../shared/qmlstreamwriter.h + ../../src/qmlcompiler/qmlstreamwriter.cpp ../../src/qmlcompiler/qmlstreamwriter_p.h main.cpp qmltypereader.cpp qmltypereader.h INCLUDE_DIRECTORIES - ../shared + ../../src/qmlcompiler PUBLIC_LIBRARIES Qt::CorePrivate Qt::Gui diff --git a/tools/qmlplugindump/CMakeLists.txt b/tools/qmlplugindump/CMakeLists.txt index cbeb03f38b..d8fdaeee8c 100644 --- a/tools/qmlplugindump/CMakeLists.txt +++ b/tools/qmlplugindump/CMakeLists.txt @@ -9,11 +9,11 @@ qt_add_tool(${target_name} TARGET_DESCRIPTION "QML Plugin Metadata Dumper" TOOLS_TARGET Qml # special case SOURCES - ../shared/qmlstreamwriter.cpp ../shared/qmlstreamwriter.h + ../../src/qmlcompiler/qmlstreamwriter.cpp ../../src/qmlcompiler/qmlstreamwriter_p.h main.cpp qmltypereader.cpp qmltypereader.h INCLUDE_DIRECTORIES - ../shared + ../../src/qmlcompiler PUBLIC_LIBRARIES Qt::CorePrivate Qt::Gui diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index ab26c5b3d5..bfc78f2974 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -60,7 +60,7 @@ #include #include "qmltypereader.h" -#include "qmlstreamwriter.h" +#include "qmlstreamwriter_p.h" #ifdef QT_SIMULATOR #include diff --git a/tools/qmlplugindump/qmlplugindump.pro b/tools/qmlplugindump/qmlplugindump.pro index 8bf40d2c8d..217fe23bbe 100644 --- a/tools/qmlplugindump/qmlplugindump.pro +++ b/tools/qmlplugindump/qmlplugindump.pro @@ -5,15 +5,18 @@ CONFIG += no_import_scan QTPLUGIN.platforms = qminimal -include(../shared/shared.pri) +# We cannot use libQmlCompiler as that is built for the host +# and qmlplugindump needs to be built for the target. + +INCLUDEPATH += $$PWD/../../src/qmlcompiler SOURCES += \ - $$QMLSTREAMWRITER_SOURCES \ + ../../src/qmlcompiler/qmlstreamwriter.cpp \ main.cpp \ qmltypereader.cpp HEADERS += \ - $$QMLSTREAMWRITER_HEADERS \ + ../../src/qmlcompiler/qmlstreamwriter_p.h \ qmltypereader.h macx { diff --git a/tools/shared/importedmembersvisitor.cpp b/tools/shared/importedmembersvisitor.cpp deleted file mode 100644 index 559c0533f7..0000000000 --- a/tools/shared/importedmembersvisitor.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/**************************************************************************** -** -** 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.h" -#include "scopetree.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(publicMember->statement); - if (const auto idExpression = cast(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(sourceElement->sourceElement)) { - // nothing to do - } else { - const auto loc = sourceElement->firstSourceLocation(); - m_errors.append( - "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(scriptBinding->statement); - const auto *idExprension = cast(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/tools/shared/importedmembersvisitor.h b/tools/shared/importedmembersvisitor.h deleted file mode 100644 index ab44deda9b..0000000000 --- a/tools/shared/importedmembersvisitor.h +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -** -** 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.h" -#include "qcoloroutput.h" - -#include - -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 m_currentObjects; - ScopeTree::ConstPtr m_rootObject; - QHash m_objects; - - QStringList m_errors; -}; - -#endif // IMPORTEDMEMBERSVISITOR_H diff --git a/tools/shared/metatypes.h b/tools/shared/metatypes.h deleted file mode 100644 index d462fb684f..0000000000 --- a/tools/shared/metatypes.h +++ /dev/null @@ -1,200 +0,0 @@ -/**************************************************************************** -** -** 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 -#include -#include - -// 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 returnType() const { return m_returnType.toStrongRef(); } - void setReturnTypeName(const QString &type) { m_returnTypeName = type; } - void setReturnType(const QSharedPointer &type) - { - m_returnType = type; - } - - QStringList parameterNames() const { return m_paramNames; } - QStringList parameterTypeNames() const { return m_paramTypeNames; } - QList> parameterTypes() const - { - QList> result; - for (const auto &type : m_paramTypes) - result.append(type.toStrongRef()); - return result; - } - void setParameterTypes(const QList> &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 &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 m_returnType; - - QStringList m_paramNames; - QStringList m_paramTypeNames; - QList> m_paramTypes; - - Type m_methodType = Signal; - Access m_methodAccess = Private; - int m_revision = 0; -}; - -class MetaProperty -{ - QString m_propertyName; - QString m_typeName; - QWeakPointer 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 &type) { m_type = type; } - QSharedPointer 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/tools/shared/qmljsimporter.cpp b/tools/shared/qmljsimporter.cpp deleted file mode 100644 index f2e8453c7c..0000000000 --- a/tools/shared/qmljsimporter.cpp +++ /dev/null @@ -1,291 +0,0 @@ -/**************************************************************************** -** -** 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.h" -#include "typedescriptionreader.h" -#include "qmljstypereader.h" - -#include - -#include -#include - -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('.')); - return prefix.isEmpty() ? name : (prefix + QLatin1Char('.') + name); -} - -static QQmlDirParser createQmldirParserForFile(const QString &filename) -{ - QFile f(filename); - f.open(QFile::ReadOnly); - QQmlDirParser parser; - parser.parse(f.readAll()); - return parser; -} - -void QmlJSImporter::readQmltypes( - const QString &filename, QHash *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, 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 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 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/tools/shared/qmljsimporter.h b/tools/shared/qmljsimporter.h deleted file mode 100644 index 6dbd5cc527..0000000000 --- a/tools/shared/qmljsimporter.h +++ /dev/null @@ -1,103 +0,0 @@ -/**************************************************************************** -** -** 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.h" -#include - -class QmlJSImporter -{ -public: - using ImportedTypes = QHash; - - 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 cppNames; - - // Names the importing component sees, including any prefixes - QHash qmlNames; - }; - - struct Import { - QHash objects; - QHash scripts; - QList imports; - QList 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 *objects); - Import readQmldir(const QString &dirname); - ScopeTree::Ptr localFile2ScopeTree(const QString &filePath); - - QStringList m_importPaths; - QHash, Import> m_seenImports; - QHash m_importedFiles; - QStringList m_warnings; -}; - -#endif // QMLJSIMPORTER_H diff --git a/tools/shared/qmljstypereader.cpp b/tools/shared/qmljstypereader.cpp deleted file mode 100644 index 4adacf96b4..0000000000 --- a/tools/shared/qmljstypereader.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** 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.h" -#include "importedmembersvisitor.h" - -#include -#include -#include -#include -#include - -#include -#include - -static QList parseHeaders(QQmlJS::AST::UiHeaderItemList *header) -{ - using namespace QQmlJS::AST; - QList imports; - - for (; header; header = header->next) { - auto import = cast(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(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(parser.rootNode()), scopeName); -} diff --git a/tools/shared/qmljstypereader.h b/tools/shared/qmljstypereader.h deleted file mode 100644 index 9afa5d46e6..0000000000 --- a/tools/shared/qmljstypereader.h +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** 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.h" - -#include - -#include -#include - -class QmlJSTypeReader -{ -public: - struct Import { - QString module; - QTypeRevision version; - QString prefix; - }; - - QmlJSTypeReader(const QString &file) : m_file(file) {} - - ScopeTree::Ptr operator()(); - QList imports() const { return m_imports; } - QStringList errors() const { return m_errors; } - -private: - QString m_file; - QList m_imports; - QStringList m_errors; -}; - -#endif // QMLJSTYPEREADER_H diff --git a/tools/shared/qmlstreamwriter.cpp b/tools/shared/qmlstreamwriter.cpp deleted file mode 100644 index b0fbc4e443..0000000000 --- a/tools/shared/qmlstreamwriter.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/**************************************************************************** -** -** 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.h" - -#include -#include - -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 > &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/tools/shared/qmlstreamwriter.h b/tools/shared/qmlstreamwriter.h deleted file mode 100644 index cb642159ea..0000000000 --- a/tools/shared/qmlstreamwriter.h +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************** -** -** 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 - -#include -#include -#include -#include -#include - -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 > &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 m_pendingLines; - int m_pendingLineLength; - bool m_maybeOneline; - QScopedPointer m_stream; -}; - -#endif // QMLSTREAMWRITER_H diff --git a/tools/shared/resourcefilemapper.cpp b/tools/shared/resourcefilemapper.cpp deleted file mode 100644 index b9cf463575..0000000000 --- a/tools/shared/resourcefilemapper.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/**************************************************************************** -** -** 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.h" - -#include -#include -#include - -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/tools/shared/resourcefilemapper.h b/tools/shared/resourcefilemapper.h deleted file mode 100644 index ed3e486149..0000000000 --- a/tools/shared/resourcefilemapper.h +++ /dev/null @@ -1,54 +0,0 @@ -/**************************************************************************** -** -** 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 - -#include -#include -#include - -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 qrcPathToFileSystemPath; -}; - -#endif // RESOURCEFILEMAPPER_H diff --git a/tools/shared/scopetree.cpp b/tools/shared/scopetree.cpp deleted file mode 100644 index 7ebf636b2a..0000000000 --- a/tools/shared/scopetree.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "scopetree.h" - -#include -#include - -#include - -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 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{}; -} - -void ScopeTree::resolveTypes(const QHash &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 paramTypes; - - for (const QString ¶mName: 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/tools/shared/scopetree.h b/tools/shared/scopetree.h deleted file mode 100644 index 1794b45373..0000000000 --- a/tools/shared/scopetree.h +++ /dev/null @@ -1,221 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SCOPETREE_H -#define SCOPETREE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. - -#include "metatypes.h" - -#include - -#include -#include -#include -#include - -#include - -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; - using WeakPtr = QWeakPointer; - using ConstPtr = QSharedPointer; - using WeakConstPtr = QWeakPointer; - - 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 &methods) { m_methods.unite(methods); } - void addMethod(const MetaMethod &method) { m_methods.insert(method.methodName(), method); } - QMultiHash methods() const { return m_methods; } - - void addEnum(const MetaEnum &fakeEnum) { m_enums.insert(fakeEnum.name(), fakeEnum); } - QHash 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 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 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 findJSIdentifier(const QString &id) const; - - QVector childScopes() const - { - return m_childScopes; - } - - void resolveTypes(const QHash &contextualTypes); - -private: - ScopeTree(ScopeType type, const ScopeTree::Ptr &parentScope = ScopeTree::Ptr()); - - QHash m_jsIdentifiers; - - QMultiHash m_methods; - QHash m_properties; - QHash m_enums; - - QVector 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 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/tools/shared/shared.pri b/tools/shared/shared.pri deleted file mode 100644 index 181cda3e8b..0000000000 --- a/tools/shared/shared.pri +++ /dev/null @@ -1,31 +0,0 @@ -INCLUDEPATH += $$PWD - -# The relevant tools need different bits and pieces. -# Furthermore, some of the classes require devtools, some not. - -RESOURCEFILEMAPPER_SOURCES = \ - $$PWD/resourcefilemapper.cpp - -RESOURCEFILEMAPPER_HEADERS = \ - $$PWD/resourcefilemapper.h - -METATYPEREADER_SOURCES = \ - $$PWD/importedmembersvisitor.cpp \ - $$PWD/qmljsimporter.cpp \ - $$PWD/qmljstypereader.cpp \ - $$PWD/scopetree.cpp \ - $$PWD/typedescriptionreader.cpp - -METATYPEREADER_HEADERS = \ - $$PWD/importedmembersvisitor.h \ - $$PWD/qmljsimporter.h \ - $$PWD/qmljstypereader.h \ - $$PWD/metatypes.h \ - $$PWD/scopetree.h \ - $$PWD/typedescriptionreader.h - -QMLSTREAMWRITER_SOURCES = \ - $$PWD/qmlstreamwriter.cpp - -QMLSTREAMWRITER_HEADERS = \ - $$PWD/qmlstreamwriter.h diff --git a/tools/shared/typedescriptionreader.cpp b/tools/shared/typedescriptionreader.cpp deleted file mode 100644 index f95e4453b3..0000000000 --- a/tools/shared/typedescriptionreader.cpp +++ /dev/null @@ -1,689 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "typedescriptionreader.h" - -#include -#include -#include - -#include - -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 *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(ast->headers->headerItem)) { - addError(SourceLocation(), tr("Expected a single import.")); - return; - } - - auto *import = cast(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(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(member); - - auto *script = cast(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(ast->statement); - if (!stmt) { - addError(ast->statement->firstSourceLocation(), tr("Expected dependency definitions")); - return; - } - auto *exp = cast(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(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(member); - auto *script = cast(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(member); - auto *script = cast(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(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(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(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(ast->statement); - if (!expStmt) { - addError(ast->statement->firstSourceLocation(), tr("Expected string after colon.")); - return QString(); - } - - auto *stringLit = cast(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(ast->statement); - if (!expStmt) { - addError(ast->statement->firstSourceLocation(), tr("Expected boolean after colon.")); - return false; - } - - auto *trueLit = cast(expStmt->expression); - auto *falseLit = cast(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(ast->statement); - if (!expStmt) { - addError(ast->statement->firstSourceLocation(), - tr("Expected numeric literal after colon.")); - return 0; - } - - auto *numericLit = cast(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(ast->statement); - if (!expStmt) { - addError(ast->statement->firstSourceLocation(), - tr("Expected numeric literal after colon.")); - return invalidVersion; - } - - auto *numericLit = cast(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(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(ast->statement); - if (!expStmt) { - addError(ast->statement->firstSourceLocation(), - tr("Expected array of strings after colon.")); - return; - } - - auto *arrayLit = cast(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(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(ast->statement); - if (!expStmt) { - addError(ast->statement->firstSourceLocation(), - tr("Expected array of numbers after colon.")); - return; - } - - auto *arrayLit = cast(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(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(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(ast->statement); - if (!expStmt) { - addError(ast->statement->firstSourceLocation(), tr("Expected expression after colon.")); - return; - } - - if (auto *objectLit = cast(expStmt->expression)) { - for (PatternPropertyList *it = objectLit->properties; it; it = it->next) { - if (PatternProperty *assignement = it->property) { - if (auto *name = cast(assignement->name)) { - metaEnum->addKey(name->id.toString()); - continue; - } - } - addError(it->firstSourceLocation(), tr("Expected strings as enum keys.")); - } - } else if (auto *arrayLit = cast(expStmt->expression)) { - for (PatternElementList *it = arrayLit->elements; it; it = it->next) { - if (PatternElement *element = it->element) { - if (auto *name = cast(element->initializer)) { - metaEnum->addKey(name->value.toString()); - continue; - } - } - addError(it->firstSourceLocation(), tr("Expected strings as enum keys.")); - } - } else { - addError(ast->statement->firstSourceLocation(), - tr("Expected either array or object literal as enum definition.")); - } -} diff --git a/tools/shared/typedescriptionreader.h b/tools/shared/typedescriptionreader.h deleted file mode 100644 index 8395f09bc4..0000000000 --- a/tools/shared/typedescriptionreader.h +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef TYPEDESCRIPTIONREADER_H -#define TYPEDESCRIPTIONREADER_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. - -#include "scopetree.h" - -#include - -// for Q_DECLARE_TR_FUNCTIONS -#include - -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 *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 *m_objects = nullptr; - QStringList *m_dependencies = nullptr; -}; - -#endif // TYPEDESCRIPTIONREADER_H -- cgit v1.2.3