diff options
Diffstat (limited to 'src/qmldom/qqmldomelements_p.h')
-rw-r--r-- | src/qmldom/qqmldomelements_p.h | 1272 |
1 files changed, 1272 insertions, 0 deletions
diff --git a/src/qmldom/qqmldomelements_p.h b/src/qmldom/qqmldomelements_p.h new file mode 100644 index 0000000000..db57da7bb2 --- /dev/null +++ b/src/qmldom/qqmldomelements_p.h @@ -0,0 +1,1272 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QQMLDOMELEMENTS_P_H +#define QQMLDOMELEMENTS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmldomitem_p.h" +#include "qqmldomconstants_p.h" +#include "qqmldomcomments_p.h" +#include "qqmldomlinewriter_p.h" + +#include <QtQml/private/qqmljsast_p.h> +#include <QtQml/private/qqmljsengine_p.h> +#include <QtQml/private/qqmlsignalnames_p.h> + +#include <QtCore/QCborValue> +#include <QtCore/QCborMap> +#include <QtCore/QMutexLocker> +#include <QtCore/QPair> + +#include <memory> +#include <private/qqmljsscope_p.h> + +#include <functional> +#include <limits> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace Dom { + +// namespace for utility methods building specific paths +// using a namespace one can reopen it and add more methods in other places +namespace Paths { +Path moduleIndexPath( + const QString &uri, int majorVersion, const ErrorHandler &errorHandler = nullptr); +Path moduleScopePath( + const QString &uri, Version version, const ErrorHandler &errorHandler = nullptr); +Path moduleScopePath( + const QString &uri, const QString &version, const ErrorHandler &errorHandler = nullptr); +inline Path moduleScopePath( + const QString &uri, const ErrorHandler &errorHandler = nullptr) +{ + return moduleScopePath(uri, QString(), errorHandler); +} +inline Path qmlDirInfoPath(const QString &path) +{ + return Path::Root(PathRoot::Top).field(Fields::qmldirWithPath).key(path); +} +inline Path qmlDirPath(const QString &path) +{ + return qmlDirInfoPath(path).field(Fields::currentItem); +} +inline Path qmldirFileInfoPath(const QString &path) +{ + return Path::Root(PathRoot::Top).field(Fields::qmldirFileWithPath).key(path); +} +inline Path qmldirFilePath(const QString &path) +{ + return qmldirFileInfoPath(path).field(Fields::currentItem); +} +inline Path qmlFileInfoPath(const QString &canonicalFilePath) +{ + return Path::Root(PathRoot::Top).field(Fields::qmlFileWithPath).key(canonicalFilePath); +} +inline Path qmlFilePath(const QString &canonicalFilePath) +{ + return qmlFileInfoPath(canonicalFilePath).field(Fields::currentItem); +} +inline Path qmlFileObjectPath(const QString &canonicalFilePath) +{ + return qmlFilePath(canonicalFilePath) + .field(Fields::components) + .key(QString()) + .index(0) + .field(Fields::objects) + .index(0); +} +inline Path qmltypesFileInfoPath(const QString &path) +{ + return Path::Root(PathRoot::Top).field(Fields::qmltypesFileWithPath).key(path); +} +inline Path qmltypesFilePath(const QString &path) +{ + return qmltypesFileInfoPath(path).field(Fields::currentItem); +} +inline Path jsFileInfoPath(const QString &path) +{ + return Path::Root(PathRoot::Top).field(Fields::jsFileWithPath).key(path); +} +inline Path jsFilePath(const QString &path) +{ + return jsFileInfoPath(path).field(Fields::currentItem); +} +inline Path qmlDirectoryInfoPath(const QString &path) +{ + return Path::Root(PathRoot::Top).field(Fields::qmlDirectoryWithPath).key(path); +} +inline Path qmlDirectoryPath(const QString &path) +{ + return qmlDirectoryInfoPath(path).field(Fields::currentItem); +} +inline Path globalScopeInfoPath(const QString &name) +{ + return Path::Root(PathRoot::Top).field(Fields::globalScopeWithName).key(name); +} +inline Path globalScopePath(const QString &name) +{ + return globalScopeInfoPath(name).field(Fields::currentItem); +} +inline Path lookupCppTypePath(const QString &name) +{ + return Path::Current(PathCurrent::Lookup).field(Fields::cppType).key(name); +} +inline Path lookupPropertyPath(const QString &name) +{ + return Path::Current(PathCurrent::Lookup).field(Fields::propertyDef).key(name); +} +inline Path lookupSymbolPath(const QString &name) +{ + return Path::Current(PathCurrent::Lookup).field(Fields::symbol).key(name); +} +inline Path lookupTypePath(const QString &name) +{ + return Path::Current(PathCurrent::Lookup).field(Fields::type).key(name); +} +inline Path loadInfoPath(const Path &el) +{ + return Path::Root(PathRoot::Env).field(Fields::loadInfo).key(el.toString()); +} +} // end namespace Paths + +class QMLDOM_EXPORT CommentableDomElement : public DomElement +{ +public: + CommentableDomElement(const Path &pathFromOwner = Path()) : DomElement(pathFromOwner) { } + CommentableDomElement(const CommentableDomElement &o) : DomElement(o), m_comments(o.m_comments) + { + } + CommentableDomElement &operator=(const CommentableDomElement &o) = default; + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override; + RegionComments &comments() { return m_comments; } + const RegionComments &comments() const { return m_comments; } + +private: + RegionComments m_comments; +}; + +class QMLDOM_EXPORT Version +{ +public: + constexpr static DomType kindValue = DomType::Version; + constexpr static qint32 Undefined = -1; + constexpr static qint32 Latest = -2; + + Version(qint32 majorVersion = Undefined, qint32 minorVersion = Undefined); + static Version fromString(QStringView v); + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const; + + bool isLatest() const; + bool isValid() const; + QString stringValue() const; + QString majorString() const + { + if (majorVersion >= 0 || majorVersion == Undefined) + return QString::number(majorVersion); + return QString(); + } + QString majorSymbolicString() const + { + if (majorVersion == Version::Latest) + return QLatin1String("Latest"); + if (majorVersion >= 0 || majorVersion == Undefined) + return QString::number(majorVersion); + return QString(); + } + QString minorString() const + { + if (minorVersion >= 0 || minorVersion == Undefined) + return QString::number(minorVersion); + return QString(); + } + int compare(const Version &o) const + { + int c = majorVersion - o.majorVersion; + if (c != 0) + return c; + return minorVersion - o.minorVersion; + } + + qint32 majorVersion; + qint32 minorVersion; +}; +inline bool operator==(const Version &v1, const Version &v2) +{ + return v1.compare(v2) == 0; +} +inline bool operator!=(const Version &v1, const Version &v2) +{ + return v1.compare(v2) != 0; +} +inline bool operator<(const Version &v1, const Version &v2) +{ + return v1.compare(v2) < 0; +} +inline bool operator<=(const Version &v1, const Version &v2) +{ + return v1.compare(v2) <= 0; +} +inline bool operator>(const Version &v1, const Version &v2) +{ + return v1.compare(v2) > 0; +} +inline bool operator>=(const Version &v1, const Version &v2) +{ + return v1.compare(v2) >= 0; +} + +class QMLDOM_EXPORT QmlUri +{ +public: + enum class Kind { Invalid, ModuleUri, DirectoryUrl, RelativePath, AbsolutePath }; + QmlUri() = default; + static QmlUri fromString(const QString &importStr); + static QmlUri fromUriString(const QString &importStr); + static QmlUri fromDirectoryString(const QString &importStr); + bool isValid() const; + bool isDirectory() const; + bool isModule() const; + QString moduleUri() const; + QString localPath() const; + QString absoluteLocalPath(const QString &basePath = QString()) const; + QUrl directoryUrl() const; + QString directoryString() const; + QString toString() const; + Kind kind() const; + + friend bool operator==(const QmlUri &i1, const QmlUri &i2) + { + return i1.m_kind == i2.m_kind && i1.m_value == i2.m_value; + } + friend bool operator!=(const QmlUri &i1, const QmlUri &i2) { return !(i1 == i2); } + +private: + QmlUri(const QUrl &url) : m_kind(Kind::DirectoryUrl), m_value(url) { } + QmlUri(Kind kind, const QString &value) : m_kind(kind), m_value(value) { } + Kind m_kind = Kind::Invalid; + std::variant<QString, QUrl> m_value; +}; + +class QMLDOM_EXPORT Import +{ + Q_DECLARE_TR_FUNCTIONS(Import) +public: + constexpr static DomType kindValue = DomType::Import; + + static Import fromUriString( + const QString &importStr, Version v = Version(), const QString &importId = QString(), + const ErrorHandler &handler = nullptr); + static Import fromFileString( + const QString &importStr, const QString &importId = QString(), + const ErrorHandler &handler = nullptr); + + Import(const QmlUri &uri = QmlUri(), Version version = Version(), + const QString &importId = QString()) + : uri(uri), version(version), importId(importId) + { + } + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const; + Path importedPath() const + { + if (uri.isDirectory()) { + QString path = uri.absoluteLocalPath(); + if (!path.isEmpty()) { + return Paths::qmlDirPath(path); + } else { + Q_ASSERT_X(false, "Import", "url imports not supported"); + return Paths::qmldirFilePath(uri.directoryString()); + } + } else { + return Paths::moduleScopePath(uri.moduleUri(), version); + } + } + Import baseImport() const { return Import { uri, version }; } + + friend bool operator==(const Import &i1, const Import &i2) + { + return i1.uri == i2.uri && i1.version == i2.version && i1.importId == i2.importId + && i1.comments == i2.comments && i1.implicit == i2.implicit; + } + friend bool operator!=(const Import &i1, const Import &i2) { return !(i1 == i2); } + + void writeOut(const DomItem &self, OutWriter &ow) const; + + static QRegularExpression importRe(); + + QmlUri uri; + Version version; + QString importId; + RegionComments comments; + bool implicit = false; +}; + +class QMLDOM_EXPORT ModuleAutoExport +{ +public: + constexpr static DomType kindValue = DomType::ModuleAutoExport; + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const + { + bool cont = true; + cont = cont && self.dvWrapField(visitor, Fields::import, import); + cont = cont && self.dvValueField(visitor, Fields::inheritVersion, inheritVersion); + return cont; + } + + friend bool operator==(const ModuleAutoExport &i1, const ModuleAutoExport &i2) + { + return i1.import == i2.import && i1.inheritVersion == i2.inheritVersion; + } + friend bool operator!=(const ModuleAutoExport &i1, const ModuleAutoExport &i2) + { + return !(i1 == i2); + } + + Import import; + bool inheritVersion = false; +}; + +class QMLDOM_EXPORT Pragma +{ +public: + constexpr static DomType kindValue = DomType::Pragma; + + Pragma(const QString &pragmaName = QString(), const QStringList &pragmaValues = {}) + : name(pragmaName), values{ pragmaValues } + { + } + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const + { + bool cont = self.dvValueField(visitor, Fields::name, name); + cont = cont && self.dvValueField(visitor, Fields::values, values); + cont = cont && self.dvWrapField(visitor, Fields::comments, comments); + return cont; + } + + void writeOut(const DomItem &self, OutWriter &ow) const; + + QString name; + QStringList values; + RegionComments comments; +}; + +class QMLDOM_EXPORT Id +{ +public: + constexpr static DomType kindValue = DomType::Id; + + Id(const QString &idName = QString(), const Path &referredObject = Path()); + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const; + void updatePathFromOwner(const Path &pathFromOwner); + Path addAnnotation(const Path &selfPathFromOwner, const QmlObject &ann, QmlObject **aPtr = nullptr); + + QString name; + Path referredObjectPath; + RegionComments comments; + QList<QmlObject> annotations; + std::shared_ptr<ScriptExpression> value; +}; + +// TODO: rename? it may contain statements and stuff, not only expressions +// TODO QTBUG-121933 +class QMLDOM_EXPORT ScriptExpression final : public OwningItem +{ + Q_GADGET + Q_DECLARE_TR_FUNCTIONS(ScriptExpression) +public: + enum class ExpressionType { + BindingExpression, + FunctionBody, + ArgInitializer, + ArgumentStructure, + ReturnType, + JSCode, // Used for storing the content of the whole .js file as "one" Expression + ESMCode, // Used for storing the content of the whole ECMAScript module (.mjs) as "one" + // Expression + }; + Q_ENUM(ExpressionType); + constexpr static DomType kindValue = DomType::ScriptExpression; + DomType kind() const override { return kindValue; } + + explicit ScriptExpression( + QStringView code, const std::shared_ptr<QQmlJS::Engine> &engine, AST::Node *ast, + const std::shared_ptr<AstComments> &comments, ExpressionType expressionType, + SourceLocation localOffset = SourceLocation(), int derivedFrom = 0, + QStringView preCode = QStringView(), QStringView postCode = QStringView()); + + ScriptExpression() + : ScriptExpression(QStringView(), std::shared_ptr<QQmlJS::Engine>(), nullptr, + std::shared_ptr<AstComments>(), ExpressionType::BindingExpression, + SourceLocation(), 0) + { + } + + explicit ScriptExpression( + const QString &code, ExpressionType expressionType, int derivedFrom = 0, + const QString &preCode = QString(), const QString &postCode = QString()) + : OwningItem(derivedFrom), m_expressionType(expressionType) + { + setCode(code, preCode, postCode); + } + + ScriptExpression(const ScriptExpression &e); + + std::shared_ptr<ScriptExpression> makeCopy(const DomItem &self) const + { + return std::static_pointer_cast<ScriptExpression>(doCopy(self)); + } + + std::shared_ptr<ScriptExpression> copyWithUpdatedCode(const DomItem &self, const QString &code) const; + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override; + + Path canonicalPath(const DomItem &self) const override { return self.m_ownerPath; } + // parsed and created if not available + AST::Node *ast() const { return m_ast; } + // dump of the ast (without locations) + void astDumper(const Sink &s, AstDumperOptions options) const; + QString astRelocatableDump() const; + + // definedSymbols name, value, from + // usedSymbols name, locations + QStringView code() const + { + QMutexLocker l(mutex()); + return m_code; + } + + ExpressionType expressionType() const + { + QMutexLocker l(mutex()); + return m_expressionType; + } + + bool isNull() const + { + QMutexLocker l(mutex()); + return m_code.isNull(); + } + std::shared_ptr<QQmlJS::Engine> engine() const + { + QMutexLocker l(mutex()); + return m_engine; + } + std::shared_ptr<AstComments> astComments() const { return m_astComments; } + void writeOut(const DomItem &self, OutWriter &lw) const override; + SourceLocation globalLocation(const DomItem &self) const; + SourceLocation localOffset() const { return m_localOffset; } + QStringView preCode() const { return m_preCode; } + QStringView postCode() const { return m_postCode; } + void setScriptElement(const ScriptElementVariant &p); + ScriptElementVariant scriptElement() { return m_element; } + +protected: + std::shared_ptr<OwningItem> doCopy(const DomItem &) const override + { + return std::make_shared<ScriptExpression>(*this); + } + + std::function<SourceLocation(SourceLocation)> locationToGlobalF(const DomItem &self) const + { + SourceLocation loc = globalLocation(self); + return [loc, this](SourceLocation x) { + return SourceLocation(x.offset - m_localOffset.offset + loc.offset, x.length, + x.startLine - m_localOffset.startLine + loc.startLine, + ((x.startLine == m_localOffset.startLine) ? x.startColumn + - m_localOffset.startColumn + loc.startColumn + : x.startColumn)); + }; + } + + SourceLocation locationToLocal(SourceLocation x) const + { + return SourceLocation( + x.offset - m_localOffset.offset, x.length, x.startLine - m_localOffset.startLine, + ((x.startLine == m_localOffset.startLine) + ? x.startColumn - m_localOffset.startColumn + : x.startColumn)); // are line and column 1 based? then we should + 1 + } + + std::function<SourceLocation(SourceLocation)> locationToLocalF(const DomItem &) const + { + return [this](SourceLocation x) { return locationToLocal(x); }; + } + +private: + enum class ParseMode { + QML, + JS, + ESM, // ECMAScript module + }; + + inline ParseMode resolveParseMode() + { + switch (m_expressionType) { + case ExpressionType::BindingExpression: + // unfortunately there are no documentation explaining this resolution + // this was just moved from the original implementation + return ParseMode::QML; + case ExpressionType::ESMCode: + return ParseMode::ESM; + default: + return ParseMode::JS; + } + } + void setCode(const QString &code, const QString &preCode, const QString &postCode); + [[nodiscard]] AST::Node *parse(ParseMode mode); + + ExpressionType m_expressionType; + QString m_codeStr; + QStringView m_code; + QStringView m_preCode; + QStringView m_postCode; + mutable std::shared_ptr<QQmlJS::Engine> m_engine; + mutable AST::Node *m_ast; + std::shared_ptr<AstComments> m_astComments; + SourceLocation m_localOffset; + ScriptElementVariant m_element; +}; + +class BindingValue; + +class QMLDOM_EXPORT Binding +{ +public: + constexpr static DomType kindValue = DomType::Binding; + + Binding(const QString &m_name = QString(), + std::unique_ptr<BindingValue> value = std::unique_ptr<BindingValue>(), + BindingType bindingType = BindingType::Normal); + Binding(const QString &m_name, const std::shared_ptr<ScriptExpression> &value, + BindingType bindingType = BindingType::Normal); + Binding(const QString &m_name, const QString &scriptCode, + BindingType bindingType = BindingType::Normal); + Binding(const QString &m_name, const QmlObject &value, + BindingType bindingType = BindingType::Normal); + Binding(const QString &m_name, const QList<QmlObject> &value, + BindingType bindingType = BindingType::Normal); + Binding(const Binding &o); + Binding(Binding &&o) = default; + ~Binding(); + Binding &operator=(const Binding &); + Binding &operator=(Binding &&) = default; + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const; + DomItem valueItem(const DomItem &self) const; // ### REVISIT: consider replacing return value with variant + BindingValueKind valueKind() const; + QString name() const { return m_name; } + BindingType bindingType() const { return m_bindingType; } + QmlObject const *objectValue() const; + QList<QmlObject> const *arrayValue() const; + std::shared_ptr<ScriptExpression> scriptExpressionValue() const; + QmlObject *objectValue(); + QList<QmlObject> *arrayValue(); + std::shared_ptr<ScriptExpression> scriptExpressionValue(); + QList<QmlObject> annotations() const { return m_annotations; } + void setAnnotations(const QList<QmlObject> &annotations) { m_annotations = annotations; } + void setValue(std::unique_ptr<BindingValue> &&value) { m_value = std::move(value); } + Path addAnnotation(const Path &selfPathFromOwner, const QmlObject &a, QmlObject **aPtr = nullptr); + const RegionComments &comments() const { return m_comments; } + RegionComments &comments() { return m_comments; } + void updatePathFromOwner(const Path &newPath); + void writeOut(const DomItem &self, OutWriter &lw) const; + void writeOutValue(const DomItem &self, OutWriter &lw) const; + bool isSignalHandler() const + { + QString baseName = m_name.split(QLatin1Char('.')).last(); + return QQmlSignalNames::isHandlerName(baseName); + } + static QString preCodeForName(QStringView n) + { + return QStringLiteral(u"QtObject{\n %1: ").arg(n.split(u'.').last()); + } + static QString postCodeForName(QStringView) { return QStringLiteral(u"\n}\n"); } + QString preCode() const { return preCodeForName(m_name); } + QString postCode() const { return postCodeForName(m_name); } + + ScriptElementVariant bindingIdentifiers() const { return m_bindingIdentifiers; } + void setBindingIdentifiers(const ScriptElementVariant &bindingIdentifiers) { m_bindingIdentifiers = bindingIdentifiers; } + +private: + friend class QQmlDomAstCreator; + BindingType m_bindingType; + QString m_name; + std::unique_ptr<BindingValue> m_value; + QList<QmlObject> m_annotations; + RegionComments m_comments; + ScriptElementVariant m_bindingIdentifiers; +}; + +class QMLDOM_EXPORT AttributeInfo +{ +public: + enum Access { Private, Protected, Public }; + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const; + + Path addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation, + QmlObject **aPtr = nullptr); + void updatePathFromOwner(const Path &newPath); + + QQmlJSScope::ConstPtr semanticScope() const { return m_semanticScope; } + void setSemanticScope(const QQmlJSScope::ConstPtr &scope) { m_semanticScope = scope; } + + QString name; + Access access = Access::Public; + QString typeName; + bool isReadonly = false; + bool isList = false; + QList<QmlObject> annotations; + RegionComments comments; + QQmlJSScope::ConstPtr m_semanticScope; +}; + +struct QMLDOM_EXPORT LocallyResolvedAlias +{ + enum class Status { Invalid, ResolvedProperty, ResolvedObject, Loop, TooDeep }; + bool valid() + { + switch (status) { + case Status::ResolvedProperty: + case Status::ResolvedObject: + return true; + default: + return false; + } + } + DomItem baseObject; + DomItem localPropertyDef; + QString typeName; + QStringList accessedPath; + Status status = Status::Invalid; + int nAliases = 0; +}; + +class QMLDOM_EXPORT PropertyDefinition : public AttributeInfo +{ +public: + constexpr static DomType kindValue = DomType::PropertyDefinition; + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const + { + bool cont = AttributeInfo::iterateDirectSubpaths(self, visitor); + cont = cont && self.dvValueField(visitor, Fields::isPointer, isPointer); + cont = cont && self.dvValueField(visitor, Fields::isFinal, isFinal); + cont = cont && self.dvValueField(visitor, Fields::isAlias, isAlias()); + cont = cont && self.dvValueField(visitor, Fields::isDefaultMember, isDefaultMember); + cont = cont && self.dvValueField(visitor, Fields::isRequired, isRequired); + cont = cont && self.dvValueField(visitor, Fields::read, read); + cont = cont && self.dvValueField(visitor, Fields::write, write); + cont = cont && self.dvValueField(visitor, Fields::bindable, bindable); + cont = cont && self.dvValueField(visitor, Fields::notify, notify); + cont = cont && self.dvReferenceField(visitor, Fields::type, typePath()); + if (m_nameIdentifiers) { + cont = cont && self.dvItemField(visitor, Fields::nameIdentifiers, [this, &self]() { + return self.subScriptElementWrapperItem(m_nameIdentifiers); + }); + } + return cont; + } + + Path typePath() const { return Paths::lookupTypePath(typeName); } + + bool isAlias() const { return typeName == u"alias"; } + bool isParametricType() const; + void writeOut(const DomItem &self, OutWriter &lw) const; + ScriptElementVariant nameIdentifiers() const { return m_nameIdentifiers; } + void setNameIdentifiers(const ScriptElementVariant &name) { m_nameIdentifiers = name; } + + QString read; + QString write; + QString bindable; + QString notify; + bool isFinal = false; + bool isPointer = false; + bool isDefaultMember = false; + bool isRequired = false; + ScriptElementVariant m_nameIdentifiers; +}; + +class QMLDOM_EXPORT PropertyInfo +{ +public: + constexpr static DomType kindValue = DomType::PropertyInfo; // used to get the correct kind in ObjectWrapper + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const; + + QList<DomItem> propertyDefs; + QList<DomItem> bindings; +}; + +class QMLDOM_EXPORT MethodParameter +{ +public: + constexpr static DomType kindValue = DomType::MethodParameter; + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const; + + void writeOut(const DomItem &self, OutWriter &ow) const; + void writeOutSignal(const DomItem &self, OutWriter &ow) const; + + QString name; + QString typeName; + bool isPointer = false; + bool isReadonly = false; + bool isList = false; + bool isRestElement = false; + std::shared_ptr<ScriptExpression> defaultValue; + /*! + \internal + Contains the scriptElement representing this argument, inclusive default value, + deconstruction, etc. + */ + std::shared_ptr<ScriptExpression> value; + QList<QmlObject> annotations; + RegionComments comments; +}; + +class QMLDOM_EXPORT MethodInfo : public AttributeInfo +{ + Q_GADGET +public: + enum MethodType { Signal, Method }; + Q_ENUM(MethodType) + + constexpr static DomType kindValue = DomType::MethodInfo; + + Path typePath(const DomItem &) const + { + return (typeName.isEmpty() ? Path() : Paths::lookupTypePath(typeName)); + } + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const; + QString preCode(const DomItem &) const; // ### REVISIT, might be simplified by using different toplevel production rules at usage site + QString postCode(const DomItem &) const; + void writePre(const DomItem &self, OutWriter &ow) const; + void writeOut(const DomItem &self, OutWriter &ow) const; + void setCode(const QString &code) + { + body = std::make_shared<ScriptExpression>( + code, ScriptExpression::ExpressionType::FunctionBody, 0, + QLatin1String("function foo(){\n"), QLatin1String("\n}\n")); + } + MethodInfo() = default; + + // TODO: make private + add getters/setters + QList<MethodParameter> parameters; + MethodType methodType = Method; + std::shared_ptr<ScriptExpression> body; + std::shared_ptr<ScriptExpression> returnType; + bool isConstructor = false; +}; + +class QMLDOM_EXPORT EnumItem +{ +public: + constexpr static DomType kindValue = DomType::EnumItem; + + EnumItem(const QString &name = QString(), int value = 0) : m_name(name), m_value(value) { } + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const; + + QString name() const { return m_name; } + double value() const { return m_value; } + RegionComments &comments() { return m_comments; } + const RegionComments &comments() const { return m_comments; } + void writeOut(const DomItem &self, OutWriter &lw) const; + +private: + QString m_name; + double m_value; + RegionComments m_comments; +}; + +class QMLDOM_EXPORT EnumDecl final : public CommentableDomElement +{ +public: + constexpr static DomType kindValue = DomType::EnumDecl; + DomType kind() const override { return kindValue; } + + EnumDecl(const QString &name = QString(), QList<EnumItem> values = QList<EnumItem>(), + Path pathFromOwner = Path()) + : CommentableDomElement(pathFromOwner), m_name(name), m_values(values) + { + } + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const override; + + QString name() const { return m_name; } + void setName(const QString &name) { m_name = name; } + const QList<EnumItem> &values() const & { return m_values; } + bool isFlag() const { return m_isFlag; } + void setIsFlag(bool flag) { m_isFlag = flag; } + QString alias() const { return m_alias; } + void setAlias(const QString &aliasName) { m_alias = aliasName; } + void setValues(QList<EnumItem> values) { m_values = values; } + Path addValue(EnumItem value) + { + m_values.append(value); + return Path::Field(Fields::values).index(index_type(m_values.size() - 1)); + } + void updatePathFromOwner(const Path &newP) override; + + const QList<QmlObject> &annotations() const & { return m_annotations; } + void setAnnotations(const QList<QmlObject> &annotations); + Path addAnnotation(const QmlObject &child, QmlObject **cPtr = nullptr); + void writeOut(const DomItem &self, OutWriter &lw) const override; + +private: + QString m_name; + bool m_isFlag = false; + QString m_alias; + QList<EnumItem> m_values; + QList<QmlObject> m_annotations; +}; + +class QMLDOM_EXPORT QmlObject final : public CommentableDomElement +{ + Q_DECLARE_TR_FUNCTIONS(QmlObject) +public: + constexpr static DomType kindValue = DomType::QmlObject; + DomType kind() const override { return kindValue; } + + QmlObject(const Path &pathFromOwner = Path()); + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override; + bool iterateBaseDirectSubpaths(const DomItem &self, DirectVisitor) const; + QList<QString> fields() const; + QList<QString> fields(const DomItem &) const override { return fields(); } + DomItem field(const DomItem &self, QStringView name) const override; + void updatePathFromOwner(const Path &newPath) override; + QString localDefaultPropertyName() const; + QString defaultPropertyName(const DomItem &self) const; + virtual bool iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &owner)> visitor) const; + + QString idStr() const { return m_idStr; } + QString name() const { return m_name; } + const QList<Path> &prototypePaths() const & { return m_prototypePaths; } + Path nextScopePath() const { return m_nextScopePath; } + const QMultiMap<QString, PropertyDefinition> &propertyDefs() const & { return m_propertyDefs; } + const QMultiMap<QString, Binding> &bindings() const & { return m_bindings; } + const QMultiMap<QString, MethodInfo> &methods() const & { return m_methods; } + QList<QmlObject> children() const { return m_children; } + QList<QmlObject> annotations() const { return m_annotations; } + + void setIdStr(const QString &id) { m_idStr = id; } + void setName(const QString &name) { m_name = name; } + void setDefaultPropertyName(const QString &name) { m_defaultPropertyName = name; } + void setPrototypePaths(QList<Path> prototypePaths) { m_prototypePaths = prototypePaths; } + Path addPrototypePath(const Path &prototypePath) + { + index_type idx = index_type(m_prototypePaths.indexOf(prototypePath)); + if (idx == -1) { + idx = index_type(m_prototypePaths.size()); + m_prototypePaths.append(prototypePath); + } + return Path::Field(Fields::prototypes).index(idx); + } + void setNextScopePath(const Path &nextScopePath) { m_nextScopePath = nextScopePath; } + void setPropertyDefs(QMultiMap<QString, PropertyDefinition> propertyDefs) + { + m_propertyDefs = propertyDefs; + } + void setBindings(QMultiMap<QString, Binding> bindings) { m_bindings = bindings; } + void setMethods(QMultiMap<QString, MethodInfo> functionDefs) { m_methods = functionDefs; } + void setChildren(const QList<QmlObject> &children) + { + m_children = children; + if (pathFromOwner()) + updatePathFromOwner(pathFromOwner()); + } + void setAnnotations(const QList<QmlObject> &annotations) + { + m_annotations = annotations; + if (pathFromOwner()) + updatePathFromOwner(pathFromOwner()); + } + Path addPropertyDef(const PropertyDefinition &propertyDef, AddOption option, + PropertyDefinition **pDef = nullptr) + { + return insertUpdatableElementInMultiMap(pathFromOwner().field(Fields::propertyDefs), + m_propertyDefs, propertyDef.name, propertyDef, + option, pDef); + } + MutableDomItem addPropertyDef(MutableDomItem &self, const PropertyDefinition &propertyDef, + AddOption option); + + Path addBinding(Binding binding, AddOption option, Binding **bPtr = nullptr) + { + return insertUpdatableElementInMultiMap(pathFromOwner().field(Fields::bindings), m_bindings, + binding.name(), binding, option, bPtr); + } + MutableDomItem addBinding(MutableDomItem &self, Binding binding, AddOption option); + Path addMethod(const MethodInfo &functionDef, AddOption option, MethodInfo **mPtr = nullptr) + { + return insertUpdatableElementInMultiMap(pathFromOwner().field(Fields::methods), m_methods, + functionDef.name, functionDef, option, mPtr); + } + MutableDomItem addMethod(MutableDomItem &self, const MethodInfo &functionDef, AddOption option); + Path addChild(QmlObject child, QmlObject **cPtr = nullptr) + { + return appendUpdatableElementInQList(pathFromOwner().field(Fields::children), m_children, + child, cPtr); + } + MutableDomItem addChild(MutableDomItem &self, QmlObject child) + { + Path p = addChild(child); + return MutableDomItem(self.owner().item(), p); + } + Path addAnnotation(const QmlObject &annotation, QmlObject **aPtr = nullptr) + { + return appendUpdatableElementInQList(pathFromOwner().field(Fields::annotations), + m_annotations, annotation, aPtr); + } + void writeOut(const DomItem &self, OutWriter &ow, const QString &onTarget) const; + void writeOut(const DomItem &self, OutWriter &lw) const override { writeOut(self, lw, QString()); } + + LocallyResolvedAlias resolveAlias(const DomItem &self, + std::shared_ptr<ScriptExpression> accessSequence) const; + LocallyResolvedAlias resolveAlias(const DomItem &self, const QStringList &accessSequence) const; + + QQmlJSScope::ConstPtr semanticScope() const { return m_scope; } + void setSemanticScope(const QQmlJSScope::ConstPtr &scope) { m_scope = scope; } + + ScriptElementVariant nameIdentifiers() const { return m_nameIdentifiers; } + void setNameIdentifiers(const ScriptElementVariant &name) { m_nameIdentifiers = name; } + +private: + friend class QQmlDomAstCreator; + QString m_idStr; + QString m_name; + QList<Path> m_prototypePaths; + Path m_nextScopePath; + QString m_defaultPropertyName; + QMultiMap<QString, PropertyDefinition> m_propertyDefs; + QMultiMap<QString, Binding> m_bindings; + QMultiMap<QString, MethodInfo> m_methods; + QList<QmlObject> m_children; + QList<QmlObject> m_annotations; + QQmlJSScope::ConstPtr m_scope; + ScriptElementVariant m_nameIdentifiers; +}; + +class Export +{ + Q_DECLARE_TR_FUNCTIONS(Export) +public: + constexpr static DomType kindValue = DomType::Export; + static Export fromString( + const Path &source, QStringView exp, const Path &typePath, const ErrorHandler &h); + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const + { + bool cont = true; + cont = cont && self.dvValueField(visitor, Fields::uri, uri); + cont = cont && self.dvValueField(visitor, Fields::typeName, typeName); + cont = cont && self.dvWrapField(visitor, Fields::version, version); + if (typePath) + cont = cont && self.dvReferenceField(visitor, Fields::type, typePath); + cont = cont && self.dvValueField(visitor, Fields::isInternal, isInternal); + cont = cont && self.dvValueField(visitor, Fields::isSingleton, isSingleton); + if (exportSourcePath) + cont = cont && self.dvReferenceField(visitor, Fields::exportSource, exportSourcePath); + return cont; + } + + Path exportSourcePath; + QString uri; + QString typeName; + Version version; + Path typePath; + bool isInternal = false; + bool isSingleton = false; +}; + +class QMLDOM_EXPORT Component : public CommentableDomElement +{ +public: + Component(const QString &name); + Component(const Path &pathFromOwner = Path()); + Component(const Component &o) = default; + Component &operator=(const Component &) = default; + + bool iterateDirectSubpaths(const DomItem &, DirectVisitor) const override; + void updatePathFromOwner(const Path &newPath) override; + DomItem field(const DomItem &self, QStringView name) const override; + + QString name() const { return m_name; } + const QMultiMap<QString, EnumDecl> &enumerations() const & { return m_enumerations; } + const QList<QmlObject> &objects() const & { return m_objects; } + bool isSingleton() const { return m_isSingleton; } + bool isCreatable() const { return m_isCreatable; } + bool isComposite() const { return m_isComposite; } + QString attachedTypeName() const { return m_attachedTypeName; } + Path attachedTypePath(const DomItem &) const { return m_attachedTypePath; } + + void setName(const QString &name) { m_name = name; } + void setEnumerations(QMultiMap<QString, EnumDecl> enumerations) + { + m_enumerations = enumerations; + } + Path addEnumeration(const EnumDecl &enumeration, AddOption option = AddOption::Overwrite, + EnumDecl **ePtr = nullptr) + { + return insertUpdatableElementInMultiMap(pathFromOwner().field(Fields::enumerations), + m_enumerations, enumeration.name(), enumeration, + option, ePtr); + } + void setObjects(const QList<QmlObject> &objects) { m_objects = objects; } + Path addObject(const QmlObject &object, QmlObject **oPtr = nullptr); + void setIsSingleton(bool isSingleton) { m_isSingleton = isSingleton; } + void setIsCreatable(bool isCreatable) { m_isCreatable = isCreatable; } + void setIsComposite(bool isComposite) { m_isComposite = isComposite; } + void setAttachedTypeName(const QString &name) { m_attachedTypeName = name; } + void setAttachedTypePath(const Path &p) { m_attachedTypePath = p; } + +private: + friend class QQmlDomAstCreator; + QString m_name; + QMultiMap<QString, EnumDecl> m_enumerations; + QList<QmlObject> m_objects; + bool m_isSingleton = false; + bool m_isCreatable = true; + bool m_isComposite = true; + QString m_attachedTypeName; + Path m_attachedTypePath; +}; + +class QMLDOM_EXPORT JsResource final : public Component +{ +public: + constexpr static DomType kindValue = DomType::JsResource; + DomType kind() const override { return kindValue; } + + JsResource(const Path &pathFromOwner = Path()) : Component(pathFromOwner) { } + bool iterateDirectSubpaths(const DomItem &, DirectVisitor) const override + { // to do: complete + return true; + } + // globalSymbols defined/exported, required/used +}; + +class QMLDOM_EXPORT QmltypesComponent final : public Component +{ +public: + constexpr static DomType kindValue = DomType::QmltypesComponent; + DomType kind() const override { return kindValue; } + + QmltypesComponent(const Path &pathFromOwner = Path()) : Component(pathFromOwner) { } + bool iterateDirectSubpaths(const DomItem &, DirectVisitor) const override; + const QList<Export> &exports() const & { return m_exports; } + QString fileName() const { return m_fileName; } + void setExports(QList<Export> exports) { m_exports = exports; } + void addExport(const Export &exportedEntry) { m_exports.append(exportedEntry); } + void setFileName(const QString &fileName) { m_fileName = fileName; } + const QList<int> &metaRevisions() const & { return m_metaRevisions; } + void setMetaRevisions(QList<int> metaRevisions) { m_metaRevisions = metaRevisions; } + void setInterfaceNames(const QStringList& interfaces) { m_interfaceNames = interfaces; } + const QStringList &interfaceNames() const & { return m_interfaceNames; } + QString extensionTypeName() const { return m_extensionTypeName; } + void setExtensionTypeName(const QString &name) { m_extensionTypeName = name; } + QString valueTypeName() const { return m_valueTypeName; } + void setValueTypeName(const QString &name) { m_valueTypeName = name; } + bool hasCustomParser() const { return m_hasCustomParser; } + void setHasCustomParser(bool v) { m_hasCustomParser = v; } + bool extensionIsJavaScript() const { return m_extensionIsJavaScript; } + void setExtensionIsJavaScript(bool v) { m_extensionIsJavaScript = v; } + bool extensionIsNamespace() const { return m_extensionIsNamespace; } + void setExtensionIsNamespace(bool v) { m_extensionIsNamespace = v; } + QQmlJSScope::AccessSemantics accessSemantics() const { return m_accessSemantics; } + void setAccessSemantics(QQmlJSScope::AccessSemantics v) { m_accessSemantics = v; } + + void setSemanticScope(const QQmlJSScope::ConstPtr &scope) { m_semanticScope = scope; } + QQmlJSScope::ConstPtr semanticScope() const { return m_semanticScope; } + +private: + QList<Export> m_exports; + QList<int> m_metaRevisions; + QString m_fileName; // remove? + QStringList m_interfaceNames; + bool m_hasCustomParser = false; + bool m_extensionIsJavaScript = false; + bool m_extensionIsNamespace = false; + QString m_valueTypeName; + QString m_extensionTypeName; + QQmlJSScope::AccessSemantics m_accessSemantics = QQmlJSScope::AccessSemantics::None; + QQmlJSScope::ConstPtr m_semanticScope; +}; + +class QMLDOM_EXPORT QmlComponent final : public Component +{ +public: + constexpr static DomType kindValue = DomType::QmlComponent; + DomType kind() const override { return kindValue; } + + QmlComponent(const QString &name = QString()) : Component(name) + { + setIsComposite(true); + setIsCreatable(true); + } + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override; + + const QMultiMap<QString, Id> &ids() const & { return m_ids; } + Path nextComponentPath() const { return m_nextComponentPath; } + void setIds(QMultiMap<QString, Id> ids) { m_ids = ids; } + void setNextComponentPath(const Path &p) { m_nextComponentPath = p; } + void updatePathFromOwner(const Path &newPath) override; + Path addId(const Id &id, AddOption option = AddOption::Overwrite, Id **idPtr = nullptr) + { + // warning does nor remove old idStr when overwriting... + return insertUpdatableElementInMultiMap(pathFromOwner().field(Fields::ids), m_ids, id.name, + id, option, idPtr); + } + void writeOut(const DomItem &self, OutWriter &) const override; + QList<QString> subComponentsNames(const DomItem &self) const; + QList<DomItem> subComponents(const DomItem &self) const; + + void setSemanticScope(const QQmlJSScope::ConstPtr &scope) { m_semanticScope = scope; } + QQmlJSScope::ConstPtr semanticScope() const { return m_semanticScope; } + ScriptElementVariant nameIdentifiers() const { return m_nameIdentifiers; } + void setNameIdentifiers(const ScriptElementVariant &name) { m_nameIdentifiers = name; } + +private: + friend class QQmlDomAstCreator; + Path m_nextComponentPath; + QMultiMap<QString, Id> m_ids; + QQmlJSScope::ConstPtr m_semanticScope; + // m_nameIdentifiers contains the name of the component as FieldMemberExpression, and therefore + // only exists in inline components! + ScriptElementVariant m_nameIdentifiers; +}; + +class QMLDOM_EXPORT GlobalComponent final : public Component +{ +public: + constexpr static DomType kindValue = DomType::GlobalComponent; + DomType kind() const override { return kindValue; } + + GlobalComponent(const Path &pathFromOwner = Path()) : Component(pathFromOwner) { } +}; + +static ErrorGroups importErrors = { { DomItem::domErrorGroup, NewErrorGroup("importError") } }; + +class QMLDOM_EXPORT ImportScope +{ + Q_DECLARE_TR_FUNCTIONS(ImportScope) +public: + constexpr static DomType kindValue = DomType::ImportScope; + + ImportScope() = default; + ~ImportScope() = default; + + const QList<Path> &importSourcePaths() const & { return m_importSourcePaths; } + + const QMap<QString, ImportScope> &subImports() const & { return m_subImports; } + + QList<Path> allSources(const DomItem &self) const; + + QSet<QString> importedNames(const DomItem &self) const + { + QSet<QString> res; + const auto sources = allSources(self); + for (const Path &p : sources) { + QSet<QString> ks = self.path(p.field(Fields::exports), self.errorHandler()).keys(); + res += ks; + } + return res; + } + + QList<DomItem> importedItemsWithName(const DomItem &self, const QString &name) const + { + QList<DomItem> res; + const auto sources = allSources(self); + for (const Path &p : sources) { + DomItem source = self.path(p.field(Fields::exports), self.errorHandler()); + DomItem els = source.key(name); + int nEls = els.indexes(); + for (int i = 0; i < nEls; ++i) + res.append(els.index(i)); + if (nEls == 0 && els) { + self.addError(importErrors.warning( + tr("Looking up '%1' expected a list of exports, not %2") + .arg(name, els.toString()))); + } + } + return res; + } + + QList<Export> importedExportsWithName(const DomItem &self, const QString &name) const + { + QList<Export> res; + for (const DomItem &i : importedItemsWithName(self, name)) + if (const Export *e = i.as<Export>()) + res.append(*e); + else + self.addError(importErrors.warning( + tr("Expected Export looking up '%1', not %2").arg(name, i.toString()))); + return res; + } + + bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const; + + void addImport(QStringList p, const Path &targetExports) + { + if (!p.isEmpty()) { + const QString current = p.takeFirst(); + m_subImports[current].addImport(std::move(p), targetExports); + } else if (!m_importSourcePaths.contains(targetExports)) { + m_importSourcePaths.append(targetExports); + } + } + +private: + QList<Path> m_importSourcePaths; + QMap<QString, ImportScope> m_subImports; +}; + +class BindingValue +{ +public: + BindingValue(); + BindingValue(const QmlObject &o); + BindingValue(const std::shared_ptr<ScriptExpression> &o); + BindingValue(const QList<QmlObject> &l); + ~BindingValue(); + BindingValue(const BindingValue &o); + BindingValue &operator=(const BindingValue &o); + + DomItem value(const DomItem &binding) const; + void updatePathFromOwner(const Path &newPath); + +private: + friend class Binding; + void clearValue(); + + BindingValueKind kind; + union { + int dummy; + QmlObject object; + std::shared_ptr<ScriptExpression> scriptExpression; + QList<QmlObject> array; + }; +}; + +} // end namespace Dom +} // end namespace QQmlJS +QT_END_NAMESPACE +#endif // QQMLDOMELEMENTS_P_H |