aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmlcompiler/qqmljsutils_p.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmlcompiler/qqmljsutils_p.h')
-rw-r--r--src/qmlcompiler/qqmljsutils_p.h391
1 files changed, 391 insertions, 0 deletions
diff --git a/src/qmlcompiler/qqmljsutils_p.h b/src/qmlcompiler/qqmljsutils_p.h
new file mode 100644
index 0000000000..eaa834bec9
--- /dev/null
+++ b/src/qmlcompiler/qqmljsutils_p.h
@@ -0,0 +1,391 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#ifndef QQMLJSUTILS_P_H
+#define QQMLJSUTILS_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 <qtqmlcompilerexports.h>
+
+#include "qqmljslogger_p.h"
+#include "qqmljsregistercontent_p.h"
+#include "qqmljsscope_p.h"
+#include "qqmljsmetatypes_p.h"
+
+#include <QtCore/qstack.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringview.h>
+#include <QtCore/qstringbuilder.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
+#include <private/qduplicatetracker_p.h>
+
+#include <optional>
+#include <functional>
+#include <type_traits>
+#include <variant>
+
+QT_BEGIN_NAMESPACE
+
+namespace detail {
+/*! \internal
+
+ Utility method that returns proper value according to the type To. This
+ version returns From.
+*/
+template<typename To, typename From, typename std::enable_if_t<!std::is_pointer_v<To>, int> = 0>
+static auto getQQmlJSScopeFromSmartPtr(const From &p) -> From
+{
+ static_assert(!std::is_pointer_v<From>, "From has to be a smart pointer holding QQmlJSScope");
+ return p;
+}
+
+/*! \internal
+
+ Utility method that returns proper value according to the type To. This
+ version returns From::get(), which is a raw pointer. The returned type
+ is not necessarily equal to To (e.g. To might be `QQmlJSScope *` while
+ returned is `const QQmlJSScope *`).
+*/
+template<typename To, typename From, typename std::enable_if_t<std::is_pointer_v<To>, int> = 0>
+static auto getQQmlJSScopeFromSmartPtr(const From &p) -> decltype(p.get())
+{
+ static_assert(!std::is_pointer_v<From>, "From has to be a smart pointer holding QQmlJSScope");
+ return p.get();
+}
+}
+
+class QQmlJSTypeResolver;
+class QQmlJSScopesById;
+struct Q_QMLCOMPILER_EXPORT QQmlJSUtils
+{
+ /*! \internal
+ Returns escaped version of \a s. This function is mostly useful for code
+ generators.
+ */
+ static QString escapeString(QString s)
+ {
+ using namespace Qt::StringLiterals;
+ return s.replace('\\'_L1, "\\\\"_L1)
+ .replace('"'_L1, "\\\""_L1)
+ .replace('\n'_L1, "\\n"_L1)
+ .replace('?'_L1, "\\?"_L1);
+ }
+
+ /*! \internal
+ Returns \a s wrapped into a literal macro specified by \a ctor. By
+ default, returns a QStringLiteral-wrapped literal. This function is
+ mostly useful for code generators.
+
+ \note This function escapes \a s before wrapping it.
+ */
+ static QString toLiteral(const QString &s, QStringView ctor = u"QStringLiteral")
+ {
+ return ctor % u"(\"" % escapeString(s) % u"\")";
+ }
+
+ /*! \internal
+ Returns \a type string conditionally wrapped into \c{const} and \c{&}.
+ This function is mostly useful for code generators.
+ */
+ static QString constRefify(QString type)
+ {
+ if (!type.endsWith(u'*'))
+ type = u"const " % type % u"&";
+ return type;
+ }
+
+ static std::optional<QQmlJSMetaProperty>
+ changeHandlerProperty(const QQmlJSScope::ConstPtr &scope, QStringView signalName)
+ {
+ if (!signalName.endsWith(QLatin1String("Changed")))
+ return {};
+ constexpr int length = int(sizeof("Changed") / sizeof(char)) - 1;
+ signalName.chop(length);
+ auto p = scope->property(signalName.toString());
+ const bool isBindable = !p.bindable().isEmpty();
+ const bool canNotify = !p.notify().isEmpty();
+ if (p.isValid() && (isBindable || canNotify))
+ return p;
+ return {};
+ }
+
+ static std::optional<QQmlJSMetaProperty>
+ propertyFromChangedHandler(const QQmlJSScope::ConstPtr &scope, QStringView changedHandler)
+ {
+ auto signalName = QQmlSignalNames::changedHandlerNameToPropertyName(changedHandler);
+ if (!signalName)
+ return {};
+
+ auto p = scope->property(*signalName);
+ const bool isBindable = !p.bindable().isEmpty();
+ const bool canNotify = !p.notify().isEmpty();
+ if (p.isValid() && (isBindable || canNotify))
+ return p;
+ return {};
+ }
+
+ static bool hasCompositeBase(const QQmlJSScope::ConstPtr &scope)
+ {
+ if (!scope)
+ return false;
+ const auto base = scope->baseType();
+ if (!base)
+ return false;
+ return base->isComposite() && base->scopeType() == QQmlSA::ScopeType::QMLScope;
+ }
+
+ enum PropertyAccessor {
+ PropertyAccessor_Read,
+ PropertyAccessor_Write,
+ };
+ /*! \internal
+
+ Returns \c true if \a p is bindable and property accessor specified by
+ \a accessor is equal to "default". Returns \c false otherwise.
+
+ \note This function follows BINDABLE-only properties logic (e.g. in moc)
+ */
+ static bool bindablePropertyHasDefaultAccessor(const QQmlJSMetaProperty &p,
+ PropertyAccessor accessor)
+ {
+ if (p.bindable().isEmpty())
+ return false;
+ switch (accessor) {
+ case PropertyAccessor::PropertyAccessor_Read:
+ return p.read() == QLatin1String("default");
+ case PropertyAccessor::PropertyAccessor_Write:
+ return p.write() == QLatin1String("default");
+ default:
+ break;
+ }
+ return false;
+ }
+
+ enum ResolvedAliasTarget {
+ AliasTarget_Invalid,
+ AliasTarget_Property,
+ AliasTarget_Object,
+ };
+ struct ResolvedAlias
+ {
+ QQmlJSMetaProperty property;
+ QQmlJSScope::ConstPtr owner;
+ ResolvedAliasTarget kind = ResolvedAliasTarget::AliasTarget_Invalid;
+ };
+ struct AliasResolutionVisitor
+ {
+ std::function<void()> reset = []() {};
+ std::function<void(const QQmlJSScope::ConstPtr &)> processResolvedId =
+ [](const QQmlJSScope::ConstPtr &) {};
+ std::function<void(const QQmlJSMetaProperty &, const QQmlJSScope::ConstPtr &)>
+ processResolvedProperty =
+ [](const QQmlJSMetaProperty &, const QQmlJSScope::ConstPtr &) {};
+ };
+ static ResolvedAlias resolveAlias(const QQmlJSTypeResolver *typeResolver,
+ const QQmlJSMetaProperty &property,
+ const QQmlJSScope::ConstPtr &owner,
+ const AliasResolutionVisitor &visitor);
+ static ResolvedAlias resolveAlias(const QQmlJSScopesById &idScopes,
+ const QQmlJSMetaProperty &property,
+ const QQmlJSScope::ConstPtr &owner,
+ const AliasResolutionVisitor &visitor);
+
+ template<typename QQmlJSScopePtr, typename Action>
+ static bool searchBaseAndExtensionTypes(QQmlJSScopePtr type, const Action &check)
+ {
+ if (!type)
+ return false;
+
+ using namespace detail;
+
+ // NB: among other things, getQQmlJSScopeFromSmartPtr() also resolves const
+ // vs non-const pointer issue, so use it's return value as the type
+ using T = decltype(getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(
+ std::declval<QQmlJSScope::ConstPtr>()));
+
+ const auto checkWrapper = [&](const auto &scope, QQmlJSScope::ExtensionKind mode) {
+ if constexpr (std::is_invocable<Action, decltype(scope),
+ QQmlJSScope::ExtensionKind>::value) {
+ return check(scope, mode);
+ } else {
+ static_assert(std::is_invocable<Action, decltype(scope)>::value,
+ "Inferred type Action has unexpected arguments");
+ Q_UNUSED(mode);
+ return check(scope);
+ }
+ };
+
+ const bool isValueOrSequenceType = [type]() {
+ switch (type->accessSemantics()) {
+ case QQmlJSScope::AccessSemantics::Value:
+ case QQmlJSScope::AccessSemantics::Sequence:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }();
+
+ QDuplicateTracker<T> seen;
+ for (T scope = type; scope && !seen.hasSeen(scope);
+ scope = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(scope->baseType())) {
+ QDuplicateTracker<T> seenExtensions;
+ // Extensions override the types they extend. However, usually base
+ // types of extensions are ignored. The unusual cases are when we
+ // have a value or sequence type or when we have the QObject type, in which
+ // case we also study the extension's base type hierarchy.
+ const bool isQObject = scope->internalName() == QLatin1String("QObject");
+ auto [extensionPtr, extensionKind] = scope->extensionType();
+ auto extension = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(extensionPtr);
+ do {
+ if (!extension || seenExtensions.hasSeen(extension))
+ break;
+
+ if (checkWrapper(extension, extensionKind))
+ return true;
+ extension = getQQmlJSScopeFromSmartPtr<QQmlJSScopePtr>(extension->baseType());
+ } while (isValueOrSequenceType || isQObject);
+
+ if (checkWrapper(scope, QQmlJSScope::NotExtension))
+ return true;
+ }
+
+ return false;
+ }
+
+ template<typename Action>
+ static void traverseFollowingQmlIrObjectStructure(const QQmlJSScope::Ptr &root, Action act)
+ {
+ // We *have* to perform DFS here: QmlIR::Object entries within the
+ // QmlIR::Document are stored in the order they appear during AST traversal
+ // (which does DFS)
+ QStack<QQmlJSScope::Ptr> stack;
+ stack.push(root);
+
+ while (!stack.isEmpty()) {
+ QQmlJSScope::Ptr current = stack.pop();
+
+ act(current);
+
+ auto children = current->childScopes();
+ // arrays are special: they are reverse-processed in QmlIRBuilder
+ if (!current->isArrayScope())
+ std::reverse(children.begin(), children.end()); // left-to-right DFS
+ stack.append(std::move(children));
+ }
+ }
+
+ /*! \internal
+
+ Traverses the base types and extensions of \a scope in the order aligned
+ with QMetaObjects created at run time for these types and extensions
+ (except that QQmlVMEMetaObject is ignored). \a start is the starting
+ type in the hierarchy where \a act is applied.
+
+ \note To call \a act for every type in the hierarchy, use
+ scope->extensionType().scope as \a start
+ */
+ template<typename Action>
+ static void traverseFollowingMetaObjectHierarchy(const QQmlJSScope::ConstPtr &scope,
+ const QQmlJSScope::ConstPtr &start, Action act)
+ {
+ // Meta objects are arranged in the following way:
+ // * static meta objects are chained first
+ // * dynamic meta objects are added on top - they come from extensions.
+ // QQmlVMEMetaObject ignored here
+ //
+ // Example:
+ // ```
+ // class A : public QObject {
+ // QML_EXTENDED(Ext)
+ // };
+ // class B : public A {
+ // QML_EXTENDED(Ext2)
+ // };
+ // ```
+ // gives: Ext2 -> Ext -> B -> A -> QObject
+ // ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^
+ // ^^^^^^^^^^^ static meta objects
+ // dynamic meta objects
+
+ using namespace Qt::StringLiterals;
+ // ignore special extensions
+ const QLatin1String ignoredExtensionNames[] = {
+ // QObject extensions: (not related to C++)
+ "Object"_L1,
+ "ObjectPrototype"_L1,
+ };
+
+ QList<QQmlJSScope::AnnotatedScope> types;
+ QList<QQmlJSScope::AnnotatedScope> extensions;
+ const auto collect = [&](const QQmlJSScope::ConstPtr &type, QQmlJSScope::ExtensionKind m) {
+ if (m == QQmlJSScope::NotExtension) {
+ types.append(QQmlJSScope::AnnotatedScope { type, m });
+ return false;
+ }
+
+ for (const auto &name : ignoredExtensionNames) {
+ if (type->internalName() == name)
+ return false;
+ }
+ extensions.append(QQmlJSScope::AnnotatedScope { type, m });
+ return false;
+ };
+ searchBaseAndExtensionTypes(scope, collect);
+
+ QList<QQmlJSScope::AnnotatedScope> all;
+ all.reserve(extensions.size() + types.size());
+ // first extensions then types
+ all.append(std::move(extensions));
+ all.append(std::move(types));
+
+ auto begin = all.cbegin();
+ // skip to start
+ while (begin != all.cend() && !begin->scope->isSameType(start))
+ ++begin;
+
+ // iterate over extensions and types starting at a specified point
+ for (; begin != all.cend(); ++begin)
+ act(begin->scope, begin->extensionSpecifier);
+ }
+
+ static std::optional<QQmlJSFixSuggestion> didYouMean(const QString &userInput,
+ QStringList candidates,
+ QQmlJS::SourceLocation location);
+
+ static std::variant<QString, QQmlJS::DiagnosticMessage>
+ sourceDirectoryPath(const QQmlJSImporter *importer, const QString &buildDirectoryPath);
+
+ template <typename Container>
+ static void deduplicate(Container &container)
+ {
+ std::sort(container.begin(), container.end());
+ auto erase = std::unique(container.begin(), container.end());
+ container.erase(erase, container.end());
+ }
+};
+
+bool Q_QMLCOMPILER_EXPORT canStrictlyCompareWithVar(
+ const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
+ const QQmlJSScope::ConstPtr &rhsType);
+
+bool Q_QMLCOMPILER_EXPORT canCompareWithQObject(
+ const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
+ const QQmlJSScope::ConstPtr &rhsType);
+
+bool Q_QMLCOMPILER_EXPORT canCompareWithQUrl(
+ const QQmlJSTypeResolver *typeResolver, const QQmlJSScope::ConstPtr &lhsType,
+ const QQmlJSScope::ConstPtr &rhsType);
+
+QT_END_NAMESPACE
+
+#endif // QQMLJSUTILS_P_H