aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmlcompiler/qqmljsscope.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmlcompiler/qqmljsscope.cpp')
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp1394
1 files changed, 1187 insertions, 207 deletions
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index aaeda2431f..b32f86226e 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -1,82 +1,87 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#include "qqmljscontextualtypes_p.h"
#include "qqmljsscope_p.h"
#include "qqmljstypereader_p.h"
#include "qqmljsimporter_p.h"
+#include "qqmljsutils_p.h"
+#include "qqmlsa.h"
+#include "qqmlsa_p.h"
#include <QtCore/qqueue.h>
#include <QtCore/qsharedpointer.h>
-#include <QtCore/qfileinfo.h>
+
+#include <private/qduplicatetracker_p.h>
#include <algorithm>
+#include <type_traits>
QT_BEGIN_NAMESPACE
-template<typename Action>
-static bool searchBaseAndExtensionTypes(const QQmlJSScope *type, const Action &check)
-{
- for (const QQmlJSScope *scope = type; scope; scope = scope->baseType().data()) {
- // Extensions override their base types
- for (const QQmlJSScope *extension = scope->extensionType().data(); extension;
- extension = extension->baseType().data()) {
- if (check(extension))
- return true;
- }
+/*!
+ \class QQmlJSScope
+ \internal
+ \brief Tracks the types for the QmlCompiler
- if (check(scope))
- return true;
- }
+ QQmlJSScope tracks the types used in qml for the QmlCompiler.
- return false;
+ Multiple QQmlJSScope objects might be created for the same conceptual type, except when reused
+ due to extensive caching. Two QQmlJSScope objects are considered equal when they are backed
+ by the same implementation, that is, they have the same internalName.
+*/
+
+using namespace Qt::StringLiterals;
+
+QQmlJSScope::QQmlJSScope(const QString &internalName) : QQmlJSScope{}
+{
+ m_internalName = internalName;
}
-QQmlJSScope::QQmlJSScope(ScopeType type, const QQmlJSScope::Ptr &parentScope)
- : m_parentScope(parentScope), m_scopeType(type) {}
+QQmlJSScope::Ptr QQmlJSScope::create(const QString &internalName)
+{
+ return QSharedPointer<QQmlJSScope>(new QQmlJSScope(internalName));
+}
-QQmlJSScope::Ptr QQmlJSScope::create(ScopeType type, const QQmlJSScope::Ptr &parentScope)
+void QQmlJSScope::reparent(const QQmlJSScope::Ptr &parentScope, const QQmlJSScope::Ptr &childScope)
{
- QSharedPointer<QQmlJSScope> childScope(new QQmlJSScope{type, parentScope});
+ if (const QQmlJSScope::Ptr parent = childScope->m_parentScope.toStrongRef())
+ parent->m_childScopes.removeOne(childScope);
if (parentScope)
- parentScope->m_childScopes.push_back(childScope);
- return childScope;
+ parentScope->m_childScopes.append(childScope);
+ childScope->m_parentScope = parentScope;
+}
+
+QQmlJSScope::Ptr QQmlJSScope::clone(const ConstPtr &origin)
+{
+ if (origin.isNull())
+ return QQmlJSScope::Ptr();
+ QQmlJSScope::Ptr cloned = create();
+ *cloned = *origin;
+ if (QQmlJSScope::Ptr parent = cloned->parentScope())
+ parent->m_childScopes.append(cloned);
+ return cloned;
+}
+
+/*!
+\internal
+Return all the JavaScript identifiers defined in the current scope.
+*/
+QHash<QString, QQmlJSScope::JavaScriptIdentifier> QQmlJSScope::ownJSIdentifiers() const
+{
+ return m_jsIdentifiers;
}
void QQmlJSScope::insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier)
{
- Q_ASSERT(m_scopeType != QQmlJSScope::QMLScope);
+ Q_ASSERT(m_scopeType != QQmlSA::ScopeType::QMLScope);
if (identifier.kind == JavaScriptIdentifier::LexicalScoped
- || identifier.kind == JavaScriptIdentifier::Injected
- || m_scopeType == QQmlJSScope::JSFunctionScope) {
+ || identifier.kind == JavaScriptIdentifier::Injected
+ || m_scopeType == QQmlSA::ScopeType::JSFunctionScope) {
m_jsIdentifiers.insert(name, identifier);
} else {
auto targetScope = parentScope();
- while (targetScope->m_scopeType != QQmlJSScope::JSFunctionScope)
+ while (targetScope->m_scopeType != QQmlSA::ScopeType::JSFunctionScope)
targetScope = targetScope->parentScope();
targetScope->m_jsIdentifiers.insert(name, identifier);
}
@@ -85,56 +90,108 @@ void QQmlJSScope::insertJSIdentifier(const QString &name, const JavaScriptIdenti
void QQmlJSScope::insertPropertyIdentifier(const QQmlJSMetaProperty &property)
{
addOwnProperty(property);
- QQmlJSMetaMethod method(property.propertyName() + QLatin1String("Changed"), QLatin1String("void"));
+ QQmlJSMetaMethod method(
+ QQmlSignalNames::propertyNameToChangedSignalName(property.propertyName()), u"void"_s);
+ method.setMethodType(QQmlJSMetaMethodType::Signal);
+ method.setIsImplicitQmlPropertyChangeSignal(true);
addOwnMethod(method);
}
-bool QQmlJSScope::isIdInCurrentScope(const QString &id) const
+bool QQmlJSScope::hasMethod(const QString &name) const
{
- return isIdInCurrentQmlScopes(id) || isIdInCurrentJSScopes(id);
+ return QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
+ if (mode == QQmlJSScope::ExtensionNamespace)
+ return false;
+ return scope->m_methods.contains(name);
+ });
}
-bool QQmlJSScope::hasMethod(const QString &name) const
+/*!
+ Returns all methods visible from this scope including those of
+ base types and extensions.
+
+ \note Methods that get shadowed are not included and only the
+ version visible from this scope is contained. Additionally method
+ overrides are not included either, only the first visible version
+ of any method is included.
+*/
+QHash<QString, QQmlJSMetaMethod> QQmlJSScope::methods() const
{
- return searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
- return scope->m_methods.contains(name);
- });
+ QHash<QString, QQmlJSMetaMethod> results;
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
+ if (mode == QQmlJSScope::ExtensionNamespace)
+ return false;
+ for (auto it = scope->m_methods.constBegin(); it != scope->m_methods.constEnd();
+ it++) {
+ if (!results.contains(it.key()))
+ results.insert(it.key(), it.value());
+ }
+ return false;
+ });
+
+ return results;
}
QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name) const
{
QList<QQmlJSMetaMethod> results;
- searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
- results.append(scope->ownMethods(name));
- return false;
- });
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
+ if (mode == QQmlJSScope::ExtensionNamespace)
+ return false;
+ results.append(scope->ownMethods(name));
+ return false;
+ });
+ return results;
+}
+
+QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name, QQmlJSMetaMethodType type) const
+{
+ QList<QQmlJSMetaMethod> results;
+
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
+ if (mode == QQmlJSScope::ExtensionNamespace)
+ return false;
+ const auto ownMethods = scope->ownMethods(name);
+ for (const auto &method : ownMethods) {
+ if (method.methodType() == type)
+ results.append(method);
+ }
+ return false;
+ });
return results;
}
bool QQmlJSScope::hasEnumeration(const QString &name) const
{
- return searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
- return scope->m_enumerations.contains(name);
- });
+ return QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope) { return scope->m_enumerations.contains(name); });
+}
+
+bool QQmlJSScope::hasOwnEnumerationKey(const QString &name) const
+{
+ for (const auto &e : m_enumerations) {
+ if (e.keys().contains(name))
+ return true;
+ }
+ return false;
}
bool QQmlJSScope::hasEnumerationKey(const QString &name) const
{
- return searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
- for (const auto &e : scope->m_enumerations) {
- if (e.keys().contains(name))
- return true;
- }
- return false;
- });
+ return QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope) { return scope->hasOwnEnumerationKey(name); });
}
QQmlJSMetaEnum QQmlJSScope::enumeration(const QString &name) const
{
QQmlJSMetaEnum result;
- searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
+ QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
const auto it = scope->m_enumerations.find(name);
if (it == scope->m_enumerations.end())
return false;
@@ -145,42 +202,99 @@ QQmlJSMetaEnum QQmlJSScope::enumeration(const QString &name) const
return result;
}
-bool QQmlJSScope::isIdInCurrentQmlScopes(const QString &id) const
+QHash<QString, QQmlJSMetaEnum> QQmlJSScope::enumerations() const
{
- if (m_scopeType == QQmlJSScope::QMLScope)
- return m_properties.contains(id) || m_methods.contains(id) || m_enumerations.contains(id);
+ QHash<QString, QQmlJSMetaEnum> results;
- const auto qmlScope = findCurrentQMLScope(parentScope());
- return qmlScope->m_properties.contains(id)
- || qmlScope->m_methods.contains(id)
- || qmlScope->m_enumerations.contains(id);
+ QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
+ for (auto it = scope->m_enumerations.constBegin(); it != scope->m_enumerations.constEnd();
+ it++) {
+ if (!results.contains(it.key()))
+ results.insert(it.key(), it.value());
+ }
+ return false;
+ });
+
+ return results;
}
-bool QQmlJSScope::isIdInCurrentJSScopes(const QString &id) const
+QString QQmlJSScope::augmentedInternalName() const
{
- if (m_scopeType != QQmlJSScope::QMLScope && m_jsIdentifiers.contains(id))
- return true;
+ using namespace Qt::StringLiterals;
- for (auto jsScope = parentScope(); jsScope; jsScope = jsScope->parentScope()) {
- if (jsScope->m_scopeType != QQmlJSScope::QMLScope && jsScope->m_jsIdentifiers.contains(id))
- return true;
+ switch (m_semantics) {
+ case AccessSemantics::Reference:
+ return m_internalName + " *"_L1;
+ case AccessSemantics::Value:
+ case AccessSemantics::Sequence:
+ break;
+ case AccessSemantics::None:
+ // If we got a namespace, it might still be a regular type, exposed as namespace.
+ // We may need to travel the inheritance chain all the way up to QObject to
+ // figure this out, since all other types may be exposed the same way.
+ for (QQmlJSScope::ConstPtr base = baseType(); base; base = base->baseType()) {
+ switch (base->accessSemantics()) {
+ case AccessSemantics::Reference:
+ return m_internalName + " *"_L1;
+ case AccessSemantics::Value:
+ case AccessSemantics::Sequence:
+ return m_internalName;
+ case AccessSemantics::None:
+ break;
+ }
+ }
+ break;
}
-
- return false;
+ return m_internalName;
}
-bool QQmlJSScope::isIdInjectedFromSignal(const QString &id) const
+QString QQmlJSScope::prettyName(QAnyStringView name)
{
- const auto found = findJSIdentifier(id);
- return found.has_value() && found->kind == JavaScriptIdentifier::Injected;
+ const auto internal = "$internal$."_L1;
+ const QString anonymous = "$anonymous$."_L1;
+
+ QString pretty = name.toString();
+
+ if (pretty.startsWith(internal))
+ pretty = pretty.mid(internal.size());
+ else if (pretty.startsWith(anonymous))
+ pretty = pretty.mid(anonymous.size());
+
+ if (pretty == u"std::nullptr_t")
+ return u"null"_s;
+
+ if (pretty == u"void")
+ return u"undefined"_s;
+
+ return pretty;
+}
+
+/*!
+ \internal
+ Returns true if the scope is the outermost element of a separate Component
+ Either because it has been implicitly wrapped, e.g. due to an assignment to
+ a Component property, or because it is the first (and only) child of a
+ Component.
+ For visitors: This method should only be called after implicit components
+ are detected, that is, after QQmlJSImportVisitor::endVisit(UiProgram *)
+ was called.
+ */
+bool QQmlJSScope::isComponentRootElement() const {
+ if (m_flags.testFlag(WrappedInImplicitComponent))
+ return true;
+
+ auto base = nonCompositeBaseType(parentScope()); // handles null parentScope()
+ if (!base)
+ return false;
+ return base->internalName() == u"QQmlComponent";
}
std::optional<QQmlJSScope::JavaScriptIdentifier>
-QQmlJSScope::findJSIdentifier(const QString &id) const
+QQmlJSScope::jsIdentifier(const QString &id) const
{
for (const auto *scope = this; scope; scope = scope->parentScope().data()) {
- if (scope->m_scopeType == QQmlJSScope::JSFunctionScope
- || scope->m_scopeType == QQmlJSScope::JSLexicalScope) {
+ if (scope->m_scopeType == QQmlSA::ScopeType::JSFunctionScope
+ || scope->m_scopeType == QQmlSA::ScopeType::JSLexicalScope) {
auto it = scope->m_jsIdentifiers.find(id);
if (it != scope->m_jsIdentifiers.end())
return *it;
@@ -190,150 +304,493 @@ QQmlJSScope::findJSIdentifier(const QString &id) const
return std::optional<JavaScriptIdentifier>{};
}
-void QQmlJSScope::resolveTypes(const QQmlJSScope::Ptr &self,
- const QHash<QString, QQmlJSScope::ConstPtr> &contextualTypes)
+std::optional<QQmlJSScope::JavaScriptIdentifier> QQmlJSScope::ownJSIdentifier(const QString &id) const
{
- auto findType = [&](const QString &name) -> QQmlJSScope::ConstPtr {
- auto type = contextualTypes.constFind(name);
- if (type != contextualTypes.constEnd())
- return *type;
+ auto it = m_jsIdentifiers.find(id);
+ if (it != m_jsIdentifiers.end())
+ return *it;
- const auto colonColon = name.indexOf(QStringLiteral("::"));
- if (colonColon > 0) {
- const auto outerType = contextualTypes.constFind(name.left(colonColon));
- if (outerType != contextualTypes.constEnd()) {
- for (const auto &innerType : qAsConst((*outerType)->m_childScopes)) {
- if (innerType->m_internalName == name)
- return innerType;
- }
+ return std::optional<JavaScriptIdentifier>{};
+}
+
+static QQmlJSScope::ImportedScope<QQmlJSScope::ConstPtr>
+qFindInlineComponents(QStringView typeName, const QQmlJS::ContextualTypes &contextualTypes)
+{
+ const int separatorIndex = typeName.lastIndexOf(u'.');
+ // do not crash in typeName.sliced() when it starts or ends with an '.'.
+ if (separatorIndex < 1 || separatorIndex >= typeName.size() - 1)
+ return {};
+
+ const auto parentIt = contextualTypes.types().constFind(typeName.first(separatorIndex).toString());
+ if (parentIt == contextualTypes.types().constEnd())
+ return {};
+
+ auto inlineComponentParent = *parentIt;
+
+ // find the inline components using BFS, as inline components defined in childrens are also
+ // accessible from other qml documents. Same for inline components defined in a base class of
+ // the parent. Use BFS over DFS as the inline components are probably not deeply-nested.
+
+ QStringView inlineComponentName = typeName.sliced(separatorIndex + 1);
+ QQueue<QQmlJSScope::ConstPtr> candidatesForInlineComponents;
+ candidatesForInlineComponents.enqueue(inlineComponentParent.scope);
+ while (candidatesForInlineComponents.size()) {
+ QQmlJSScope::ConstPtr current = candidatesForInlineComponents.dequeue();
+ if (!current) // if some type was not resolved, ignore it instead of crashing
+ continue;
+ if (current->isInlineComponent() && current->inlineComponentName() == inlineComponentName) {
+ return { current, inlineComponentParent.revision };
+ }
+ // check alternatively the inline components at layer 1 in current and basetype, then at
+ // layer 2, etc...
+ candidatesForInlineComponents.append(current->childScopes());
+ if (const auto base = current->baseType())
+ candidatesForInlineComponents.enqueue(base);
+ }
+ return {};
+}
+
+/*! \internal
+ * Finds a type in contextualTypes with given name.
+ * If a type is found, then its name is inserted into usedTypes (when provided).
+ * If contextualTypes has mode INTERNAl, then namespace resolution for enums is
+ * done (eg for Qt::Alignment).
+ * If contextualTypes has mode QML, then inline component resolution is done
+ * ("qmlFileName.IC" is correctly resolved from qmlFileName).
+ */
+QQmlJSScope::ImportedScope<QQmlJSScope::ConstPtr> QQmlJSScope::findType(
+ const QString &name, const QQmlJS::ContextualTypes &contextualTypes,
+ QSet<QString> *usedTypes)
+{
+ const auto useType = [&]() {
+ if (usedTypes != nullptr)
+ usedTypes->insert(name);
+ };
+
+ auto type = contextualTypes.types().constFind(name);
+
+ if (type != contextualTypes.types().constEnd()) {
+ useType();
+ return *type;
+ }
+
+ const auto findListType = [&](const QString &prefix, const QString &postfix)
+ -> ImportedScope<ConstPtr> {
+ if (name.startsWith(prefix) && name.endsWith(postfix)) {
+ const qsizetype prefixLength = prefix.length();
+ const QString &elementName
+ = name.mid(prefixLength, name.length() - prefixLength - postfix.length());
+ const ImportedScope<ConstPtr> element
+ = findType(elementName, contextualTypes, usedTypes);
+ if (element.scope) {
+ useType();
+ return { element.scope->listType(), element.revision };
}
}
- return QQmlJSScope::ConstPtr();
+ return {};
};
- if (!self->m_baseType && !self->m_baseTypeName.isEmpty())
- self->m_baseType = findType(self->m_baseTypeName);
+ switch (contextualTypes.context()) {
+ case QQmlJS::ContextualTypes::INTERNAL: {
+ if (const auto listType = findListType(u"QList<"_s, u">"_s);
+ listType.scope && !listType.scope->isReferenceType()) {
+ return listType;
+ }
+
+ if (const auto listType = findListType(u"QQmlListProperty<"_s, u">"_s);
+ listType.scope && listType.scope->isReferenceType()) {
+ return listType;
+ }
+
+ // look for c++ namescoped enums!
+ const auto colonColon = name.lastIndexOf(QStringLiteral("::"));
+ if (colonColon == -1)
+ break;
+
+ const QString outerTypeName = name.left(colonColon);
+ const auto outerType = contextualTypes.types().constFind(outerTypeName);
+ if (outerType == contextualTypes.types().constEnd())
+ break;
+
+ for (const auto &innerType : std::as_const(outerType->scope->m_childScopes)) {
+ if (innerType->m_internalName == name) {
+ useType();
+ return { innerType, outerType->revision };
+ }
+ }
+
+ break;
+ }
+ case QQmlJS::ContextualTypes::QML: {
+ // look after inline components
+ const auto inlineComponent = qFindInlineComponents(name, contextualTypes);
+ if (inlineComponent.scope) {
+ useType();
+ return inlineComponent;
+ }
+
+ if (const auto listType = findListType(u"list<"_s, u">"_s); listType.scope)
+ return listType;
+
+ break;
+ }
+ }
+ return {};
+}
+
+QTypeRevision QQmlJSScope::resolveType(
+ const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &context,
+ QSet<QString> *usedTypes)
+{
+ if (self->accessSemantics() == AccessSemantics::Sequence
+ && self->internalName().startsWith(u"QQmlListProperty<"_s)) {
+ self->setIsListProperty(true);
+ }
+
+ const QString baseTypeName = self->baseTypeName();
+ const auto baseType = findType(baseTypeName, context, usedTypes);
+ if (!self->m_baseType.scope && !baseTypeName.isEmpty())
+ self->m_baseType = { baseType.scope, baseType.revision };
if (!self->m_attachedType && !self->m_attachedTypeName.isEmpty())
- self->m_attachedType = findType(self->m_attachedTypeName);
+ self->m_attachedType = findType(self->m_attachedTypeName, context, usedTypes).scope;
if (!self->m_valueType && !self->m_valueTypeName.isEmpty())
- self->m_valueType = findType(self->m_valueTypeName);
-
- if (!self->m_extensionType && !self->m_extensionTypeName.isEmpty())
- self->m_extensionType = findType(self->m_extensionTypeName);
+ self->m_valueType = findType(self->m_valueTypeName, context, usedTypes).scope;
- const auto intType = findType(QStringLiteral("int"));
- Q_ASSERT(intType); // There always has to be a builtin "int" type
- for (auto it = self->m_enumerations.begin(), end = self->m_enumerations.end();
- it != end; ++it) {
- if (it->type())
- continue;
- auto enumScope = QQmlJSScope::create(EnumScope, self);
- enumScope->m_baseTypeName = QStringLiteral("int");
- enumScope->m_baseType = intType;
- enumScope->m_semantics = AccessSemantics::Value;
- enumScope->m_internalName = self->internalName() + QStringLiteral("::") + it->name();
- it->setType(ConstPtr(enumScope));
+ if (!self->m_extensionType) {
+ if (self->m_extensionTypeName.isEmpty()) {
+ if (self->accessSemantics() == AccessSemantics::Sequence) {
+ // All sequence types are implicitly extended by JS Array.
+ self->setExtensionTypeName(u"Array"_s);
+ self->setExtensionIsJavaScript(true);
+ self->m_extensionType = context.arrayType();
+ }
+ } else {
+ self->m_extensionType = findType(self->m_extensionTypeName, context, usedTypes).scope;
+ }
}
+
for (auto it = self->m_properties.begin(), end = self->m_properties.end(); it != end; ++it) {
const QString typeName = it->typeName();
if (it->type() || typeName.isEmpty())
continue;
- if (const auto type = findType(typeName)) {
- it->setType(type);
+ if (const auto type = findType(typeName, context, usedTypes); type.scope) {
+ it->setType(it->isList() ? type.scope->listType() : type.scope);
continue;
}
const auto enumeration = self->m_enumerations.find(typeName);
- if (enumeration != self->m_enumerations.end())
- it->setType(enumeration->type());
+ if (enumeration != self->m_enumerations.end()) {
+ it->setType(it->isList()
+ ? enumeration->type()->listType()
+ : QQmlJSScope::ConstPtr(enumeration->type()));
+ }
}
+ const auto resolveParameter = [&](QQmlJSMetaParameter &parameter) {
+ if (const QString typeName = parameter.typeName();
+ !parameter.type() && !typeName.isEmpty()) {
+ auto type = findType(typeName, context, usedTypes);
+ if (type.scope && parameter.isList()) {
+ type.scope = type.scope->listType();
+ parameter.setIsList(false);
+ parameter.setIsPointer(false);
+ parameter.setTypeName(type.scope ? type.scope->internalName() : QString());
+ } else if (type.scope && type.scope->isReferenceType()) {
+ parameter.setIsPointer(true);
+ }
+ parameter.setType({ type.scope });
+ }
+ };
+
for (auto it = self->m_methods.begin(), end = self->m_methods.end(); it != end; ++it) {
- const QString returnTypeName = it->returnTypeName();
- if (!it->returnType() && !returnTypeName.isEmpty())
- it->setReturnType(findType(returnTypeName));
-
- const auto paramTypeNames = it->parameterTypeNames();
- QList<QSharedPointer<const QQmlJSScope>> paramTypes = it->parameterTypes();
- if (paramTypes.length() < paramTypeNames.length())
- paramTypes.resize(paramTypeNames.length());
-
- for (int i = 0, length = paramTypes.length(); i < length; ++i) {
- auto &paramType = paramTypes[i];
- const auto paramTypeName = paramTypeNames[i];
- if (!paramType && !paramTypeName.isEmpty())
- paramType = findType(paramTypeName);
+ auto returnValue = it->returnValue();
+ resolveParameter(returnValue);
+ it->setReturnValue(returnValue);
+
+ auto parameters = it->parameters();
+ for (int i = 0, length = parameters.size(); i < length; ++i)
+ resolveParameter(parameters[i]);
+ it->setParameters(parameters);
+ }
+
+ for (auto it = self->m_jsIdentifiers.begin(); it != self->m_jsIdentifiers.end(); ++it) {
+ if (it->typeName)
+ it->scope = findType(it->typeName.value(), context, usedTypes).scope;
+ }
+
+ return baseType.revision;
+}
+
+void QQmlJSScope::updateChildScope(
+ const QQmlJSScope::Ptr &childScope, const QQmlJSScope::Ptr &self,
+ const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
+{
+ switch (childScope->scopeType()) {
+ case QQmlSA::ScopeType::GroupedPropertyScope:
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ self.data(), [&](const QQmlJSScope *type, QQmlJSScope::ExtensionKind mode) {
+ if (mode == QQmlJSScope::ExtensionNamespace)
+ return false;
+ const auto propertyIt = type->m_properties.find(childScope->internalName());
+ if (propertyIt != type->m_properties.end()) {
+ childScope->m_baseType.scope = QQmlJSScope::ConstPtr(propertyIt->type());
+ if (propertyIt->type())
+ childScope->m_semantics = propertyIt->type()->accessSemantics();
+ childScope->setBaseTypeName(propertyIt->typeName());
+ return true;
+ }
+ return false;
+ });
+ break;
+ case QQmlSA::ScopeType::AttachedPropertyScope:
+ if (const auto attachedBase = findType(
+ childScope->internalName(), contextualTypes, usedTypes).scope) {
+ childScope->m_baseType.scope = attachedBase->attachedType();
+ childScope->setBaseTypeName(attachedBase->attachedTypeName());
}
+ break;
+ default:
+ break;
+ }
+}
- it->setParameterTypes(paramTypes);
+template<typename Resolver, typename ChildScopeUpdater>
+static QTypeRevision resolveTypesInternal(
+ Resolver resolve, ChildScopeUpdater update, const QQmlJSScope::Ptr &self,
+ const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
+{
+ const QTypeRevision revision = resolve(self, contextualTypes, usedTypes);
+ // NB: constness ensures no detach
+ const auto childScopes = self->childScopes();
+ for (auto it = childScopes.begin(), end = childScopes.end(); it != end; ++it) {
+ const auto childScope = *it;
+ update(childScope, self, contextualTypes, usedTypes);
+ resolveTypesInternal(resolve, update, childScope, contextualTypes, usedTypes); // recursion
}
+ return revision;
+}
- for (auto it = self->m_childScopes.begin(), end = self->m_childScopes.end(); it != end; ++it) {
- QQmlJSScope::Ptr childScope = *it;
- switch (childScope->scopeType()) {
- case QQmlJSScope::GroupedPropertyScope:
- searchBaseAndExtensionTypes(self.data(), [&](const QQmlJSScope *type) {
- const auto propertyIt = type->m_properties.find(childScope->internalName());
- if (propertyIt != type->m_properties.end()) {
- childScope->m_baseType = QQmlJSScope::ConstPtr(propertyIt->type());
- childScope->m_baseTypeName = propertyIt->typeName();
- return true;
- }
- return false;
- });
- break;
- case QQmlJSScope::AttachedPropertyScope:
- if (const auto attachedBase = findType(childScope->internalName())) {
- childScope->m_baseType = attachedBase->attachedType();
- childScope->m_baseTypeName = attachedBase->attachedTypeName();
- }
- break;
- default:
- break;
+QTypeRevision QQmlJSScope::resolveTypes(
+ const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
+ QSet<QString> *usedTypes)
+{
+ const auto resolveAll = [](const QQmlJSScope::Ptr &self,
+ const QQmlJS::ContextualTypes &contextualTypes,
+ QSet<QString> *usedTypes) {
+ resolveEnums(self, contextualTypes, usedTypes);
+ resolveList(self, contextualTypes.arrayType());
+ return resolveType(self, contextualTypes, usedTypes);
+ };
+ return resolveTypesInternal(resolveAll, updateChildScope, self, contextualTypes, usedTypes);
+}
+
+void QQmlJSScope::resolveNonEnumTypes(
+ const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
+ QSet<QString> *usedTypes)
+{
+ resolveTypesInternal(resolveType, updateChildScope, self, contextualTypes, usedTypes);
+}
+
+static QString flagStorage(const QString &underlyingType)
+{
+ // All numeric types are builtins. Therefore we can exhaustively check the internal names.
+
+ if (underlyingType == u"uint"
+ || underlyingType == u"quint8"
+ || underlyingType == u"ushort"
+ || underlyingType == u"ulonglong") {
+ return u"uint"_s;
+ }
+
+ if (underlyingType == u"int"
+ || underlyingType == u"qint8"
+ || underlyingType == u"short"
+ || underlyingType == u"longlong") {
+ return u"int"_s;
+ }
+
+ // Will fail to resolve and produce an error on usage.
+ // It's harmless if you never use the enum.
+ return QString();
+}
+
+/*!
+ \internal
+ Resolves all enums of self.
+
+ Some enums happen to have an alias, e.g. when an enum is used as a flag, the enum will exist in
+ two versions, once as enum (e.g. Qt::MouseButton) and once as a flag (e.g. Qt::MouseButtons). In
+ this case, normally only the flag is exposed to the qt metatype system and tools like qmltc will
+ have troubles when encountering the enum in signal parameters etc. To solve this problem,
+ resolveEnums() will create a QQmlJSMetaEnum copy for the alias in case the 'self'-scope already
+ does not have an enum called like the alias.
+ */
+void QQmlJSScope::resolveEnums(
+ const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
+ QSet<QString> *usedTypes)
+{
+ // temporary hash to avoid messing up m_enumerations while iterators are active on it
+ QHash<QString, QQmlJSMetaEnum> toBeAppended;
+ for (auto it = self->m_enumerations.begin(), end = self->m_enumerations.end(); it != end; ++it) {
+ if (it->type())
+ continue;
+ QQmlJSScope::Ptr enumScope = QQmlJSScope::create();
+ reparent(self, enumScope);
+ enumScope->m_scopeType = QQmlSA::ScopeType::EnumScope;
+
+ QString typeName = it->typeName();
+ if (typeName.isEmpty())
+ typeName = QStringLiteral("int");
+ else if (it->isFlag())
+ typeName = flagStorage(typeName);
+ enumScope->setBaseTypeName(typeName);
+ const auto type = findType(typeName, contextualTypes, usedTypes);
+ enumScope->m_baseType = { type.scope, type.revision };
+
+ enumScope->m_semantics = AccessSemantics::Value;
+ enumScope->m_internalName = self->internalName() + QStringLiteral("::") + it->name();
+ if (QString alias = it->alias(); !alias.isEmpty()
+ && self->m_enumerations.constFind(alias) == self->m_enumerations.constEnd()) {
+ auto aliasScope = QQmlJSScope::clone(enumScope);
+ aliasScope->m_internalName = self->internalName() + QStringLiteral("::") + alias;
+ QQmlJSMetaEnum cpy(*it);
+ cpy.setType(QQmlJSScope::ConstPtr(aliasScope));
+ toBeAppended.insert(alias, cpy);
}
- resolveTypes(childScope, contextualTypes);
+ it->setType(QQmlJSScope::ConstPtr(enumScope));
+ }
+ // no more iterators active on m_enumerations, so it can be changed safely now
+ self->m_enumerations.insert(toBeAppended);
+}
+
+void QQmlJSScope::resolveList(const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &arrayType)
+{
+ if (self->listType() || self->accessSemantics() == AccessSemantics::Sequence)
+ return;
+
+ Q_ASSERT(!arrayType.isNull());
+ QQmlJSScope::Ptr listType = QQmlJSScope::create();
+ listType->setAccessSemantics(AccessSemantics::Sequence);
+ listType->setValueTypeName(self->internalName());
+
+ if (self->isComposite()) {
+ // There is no internalName for this thing. Just set the value type right away
+ listType->setInternalName(u"QQmlListProperty<>"_s);
+ listType->m_valueType = QQmlJSScope::ConstPtr(self);
+ } else if (self->isReferenceType()) {
+ listType->setInternalName(u"QQmlListProperty<%2>"_s.arg(self->internalName()));
+ // Do not set a filePath on the list type, so that we have to generalize it
+ // even in direct mode.
+ } else {
+ listType->setInternalName(u"QList<%2>"_s.arg(self->internalName()));
+ listType->setFilePath(self->filePath());
}
+
+ const QQmlJSImportedScope element = {self, QTypeRevision()};
+ const QQmlJSImportedScope array = {arrayType, QTypeRevision()};
+ QQmlJS::ContextualTypes contextualTypes(
+ QQmlJS::ContextualTypes::INTERNAL, { { self->internalName(), element }, },
+ arrayType);
+ QQmlJSScope::resolveTypes(listType, contextualTypes);
+
+ Q_ASSERT(listType->valueType() == self);
+ self->m_listType = listType;
+}
+
+void QQmlJSScope::resolveGeneralizedGroup(
+ const Ptr &self, const ConstPtr &baseType,
+ const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
+{
+ Q_ASSERT(baseType);
+ // Generalized group properties are always composite,
+ // which means we expect contextualTypes to be QML names.
+ Q_ASSERT(self->isComposite());
+
+ self->m_baseType.scope = baseType;
+ self->m_semantics = baseType->accessSemantics();
+ resolveNonEnumTypes(self, contextualTypes, usedTypes);
}
QQmlJSScope::ConstPtr QQmlJSScope::findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope)
{
auto qmlScope = scope;
- while (qmlScope && qmlScope->m_scopeType != QQmlJSScope::QMLScope)
+ while (qmlScope && qmlScope->m_scopeType != QQmlSA::ScopeType::QMLScope)
qmlScope = qmlScope->parentScope();
return qmlScope;
}
-void QQmlJSScope::addExport(const QString &name, const QString &package,
- const QTypeRevision &version)
-{
- m_exports.append(Export(package, name, version));
-}
-
bool QQmlJSScope::hasProperty(const QString &name) const
{
- return searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
- return scope->m_properties.contains(name);
- });
+ return QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
+ if (mode == QQmlJSScope::ExtensionNamespace)
+ return false;
+ return scope->m_properties.contains(name);
+ });
}
QQmlJSMetaProperty QQmlJSScope::property(const QString &name) const
{
QQmlJSMetaProperty prop;
- searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
- const auto it = scope->m_properties.find(name);
- if (it == scope->m_properties.end())
- return false;
- prop = *it;
- return true;
- });
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
+ if (mode == QQmlJSScope::ExtensionNamespace)
+ return false;
+ const auto it = scope->m_properties.find(name);
+ if (it == scope->m_properties.end())
+ return false;
+ prop = *it;
+ return true;
+ });
return prop;
}
+/*!
+ Returns all properties visible from this scope including those of
+ base types and extensions.
+
+ \note Properties that get shadowed are not included and only the
+ version visible from this scope is contained.
+*/
+QHash<QString, QQmlJSMetaProperty> QQmlJSScope::properties() const
+{
+ QHash<QString, QQmlJSMetaProperty> results;
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
+ if (mode == QQmlJSScope::ExtensionNamespace)
+ return false;
+ for (auto it = scope->m_properties.constBegin();
+ it != scope->m_properties.constEnd(); it++) {
+ if (!results.contains(it.key()))
+ results.insert(it.key(), it.value());
+ }
+ return false;
+ });
+ return results;
+}
+
+QQmlJSScope::AnnotatedScope QQmlJSScope::ownerOfProperty(const QQmlJSScope::ConstPtr &self,
+ const QString &name)
+{
+ QQmlJSScope::AnnotatedScope owner;
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ self, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
+ if (mode == QQmlJSScope::ExtensionNamespace)
+ return false;
+ if (scope->hasOwnProperty(name)) {
+ owner = { scope, mode };
+ return true;
+ }
+ return false;
+ });
+ return owner;
+}
+
void QQmlJSScope::setPropertyLocallyRequired(const QString &name, bool isRequired)
{
if (!isRequired)
@@ -345,17 +802,23 @@ void QQmlJSScope::setPropertyLocallyRequired(const QString &name, bool isRequire
bool QQmlJSScope::isPropertyRequired(const QString &name) const
{
bool isRequired = false;
- searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
- if (scope->isPropertyLocallyRequired(name)) {
- isRequired = true;
- return true;
- }
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
+ if (scope->isPropertyLocallyRequired(name)) {
+ isRequired = true;
+ return true;
+ }
- // If it has a property of that name, and that is not required, then none of the
- // base types matter. You cannot make a derived type's property required with
- // a "required" specification in a base type.
- return scope->hasOwnProperty(name);
- });
+ // the hasOwnProperty() below only makes sense if our scope is
+ // not an extension namespace
+ if (mode == QQmlJSScope::ExtensionNamespace)
+ return false;
+
+ // If it has a property of that name, and that is not required, then none of the
+ // base types matter. You cannot make a derived type's property required with
+ // a "required" specification in a base type.
+ return scope->hasOwnProperty(name);
+ });
return isRequired;
}
@@ -364,10 +827,314 @@ bool QQmlJSScope::isPropertyLocallyRequired(const QString &name) const
return m_requiredPropertyNames.contains(name);
}
-QQmlJSScope::Export::Export(QString package, QString type, const QTypeRevision &version) :
- m_package(std::move(package)),
- m_type(std::move(type)),
- m_version(version)
+void QQmlJSScope::addOwnPropertyBinding(const QQmlJSMetaPropertyBinding &binding, BindingTargetSpecifier specifier)
+{
+ Q_ASSERT(binding.sourceLocation().isValid());
+ m_propertyBindings.insert(binding.propertyName(), binding);
+
+ // NB: insert() prepends \a binding to the list of bindings, but we need
+ // append, so rotate
+ using iter = typename QMultiHash<QString, QQmlJSMetaPropertyBinding>::iterator;
+ QPair<iter, iter> r = m_propertyBindings.equal_range(binding.propertyName());
+ std::rotate(r.first, std::next(r.first), r.second);
+
+ // additionally store bindings in the QmlIR compatible order
+ addOwnPropertyBindingInQmlIROrder(binding, specifier);
+ Q_ASSERT(m_propertyBindings.size() == m_propertyBindingsArray.size());
+}
+
+void QQmlJSScope::addOwnPropertyBindingInQmlIROrder(const QQmlJSMetaPropertyBinding &binding,
+ BindingTargetSpecifier specifier)
+{
+ // the order:
+ // * ordinary bindings are prepended to the binding array
+ // * list bindings are properly ordered within each other, so basically
+ // prepended "in bulk"
+ // * bindings to default properties (which are not explicitly mentioned in
+ // binding expression) are inserted by source location's offset
+
+ static_assert(QTypeInfo<QQmlJSScope::QmlIRCompatibilityBindingData>::isRelocatable,
+ "We really want T to be relocatable as it improves QList<T> performance");
+
+ switch (specifier) {
+ case BindingTargetSpecifier::SimplePropertyTarget: {
+ m_propertyBindingsArray.emplaceFront(binding.propertyName(),
+ binding.sourceLocation().offset);
+ break;
+ }
+ case BindingTargetSpecifier::ListPropertyTarget: {
+ const auto bindingOnTheSameProperty =
+ [&](const QQmlJSScope::QmlIRCompatibilityBindingData &x) {
+ return x.propertyName == binding.propertyName();
+ };
+ // fake "prepend in bulk" by appending a list binding to the sequence of
+ // bindings to the same property. there's an implicit QML language
+ // guarantee that such sequence does not contain arbitrary in-between
+ // bindings that do not belong to the same list property
+ auto pos = std::find_if_not(m_propertyBindingsArray.begin(), m_propertyBindingsArray.end(),
+ bindingOnTheSameProperty);
+ Q_ASSERT(pos == m_propertyBindingsArray.begin()
+ || std::prev(pos)->propertyName == binding.propertyName());
+ m_propertyBindingsArray.emplace(pos, binding.propertyName(),
+ binding.sourceLocation().offset);
+ break;
+ }
+ case BindingTargetSpecifier::UnnamedPropertyTarget: {
+ // see QmlIR::PoolList<>::findSortedInsertionPoint()
+ const auto findInsertionPoint = [this](const QQmlJSMetaPropertyBinding &x) {
+ qsizetype pos = -1;
+ for (auto it = m_propertyBindingsArray.cbegin(); it != m_propertyBindingsArray.cend();
+ ++it) {
+ if (!(it->sourceLocationOffset <= x.sourceLocation().offset))
+ break;
+ ++pos;
+ }
+ return pos;
+ };
+
+ // see QmlIR::PoolList<>::insertAfter()
+ const auto insertAfter = [this](qsizetype pos, const QQmlJSMetaPropertyBinding &x) {
+ if (pos == -1) {
+ m_propertyBindingsArray.emplaceFront(x.propertyName(), x.sourceLocation().offset);
+ } else if (pos == m_propertyBindingsArray.size()) {
+ m_propertyBindingsArray.emplaceBack(x.propertyName(), x.sourceLocation().offset);
+ } else {
+ // since we insert *after*, use (pos + 1) as insertion point
+ m_propertyBindingsArray.emplace(pos + 1, x.propertyName(),
+ x.sourceLocation().offset);
+ }
+ };
+
+ const qsizetype insertionPos = findInsertionPoint(binding);
+ insertAfter(insertionPos, binding);
+ break;
+ }
+ default: {
+ Q_UNREACHABLE();
+ break;
+ }
+ }
+}
+
+QList<QQmlJSMetaPropertyBinding> QQmlJSScope::ownPropertyBindingsInQmlIROrder() const
+{
+ QList<QQmlJSMetaPropertyBinding> qmlIrOrdered;
+ qmlIrOrdered.reserve(m_propertyBindingsArray.size());
+
+ for (const auto &data : m_propertyBindingsArray) {
+ const auto [first, last] = m_propertyBindings.equal_range(data.propertyName);
+ Q_ASSERT(first != last);
+ auto binding = std::find_if(first, last, [&](const QQmlJSMetaPropertyBinding &x) {
+ return x.sourceLocation().offset == data.sourceLocationOffset;
+ });
+ Q_ASSERT(binding != last);
+ qmlIrOrdered.append(*binding);
+ }
+
+ return qmlIrOrdered;
+}
+
+bool QQmlJSScope::hasPropertyBindings(const QString &name) const
+{
+ return QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
+ if (mode != QQmlJSScope::NotExtension) {
+ Q_ASSERT(!scope->hasOwnPropertyBindings(name));
+ return false;
+ }
+ return scope->hasOwnPropertyBindings(name);
+ });
+}
+
+QList<QQmlJSMetaPropertyBinding> QQmlJSScope::propertyBindings(const QString &name) const
+{
+ QList<QQmlJSMetaPropertyBinding> bindings;
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
+ if (mode != QQmlJSScope::NotExtension) {
+ Q_ASSERT(!scope->hasOwnPropertyBindings(name));
+ return false;
+ }
+ const auto range = scope->ownPropertyBindings(name);
+ for (auto it = range.first; it != range.second; ++it)
+ bindings.append(*it);
+ return false;
+ });
+ return bindings;
+}
+
+bool QQmlJSScope::hasInterface(const QString &name) const
+{
+ return QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
+ if (mode != QQmlJSScope::NotExtension)
+ return false;
+ return scope->m_interfaceNames.contains(name);
+ });
+}
+
+bool QQmlJSScope::isNameDeferred(const QString &name) const
+{
+ bool isDeferred = false;
+
+ QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
+ const QStringList immediate = scope->ownImmediateNames();
+ if (!immediate.isEmpty()) {
+ isDeferred = !immediate.contains(name);
+ return true;
+ }
+ const QStringList deferred = scope->ownDeferredNames();
+ if (!deferred.isEmpty()) {
+ isDeferred = deferred.contains(name);
+ return true;
+ }
+ return false;
+ });
+
+ return isDeferred;
+}
+
+void QQmlJSScope::setBaseTypeName(const QString &baseTypeName)
+{
+ m_flags.setFlag(HasBaseTypeError, false);
+ m_baseTypeNameOrError = baseTypeName;
+}
+
+QString QQmlJSScope::baseTypeName() const
+{
+ return m_flags.testFlag(HasBaseTypeError) ? QString() : m_baseTypeNameOrError;
+}
+
+void QQmlJSScope::setBaseTypeError(const QString &baseTypeError)
+{
+ m_flags.setFlag(HasBaseTypeError);
+ m_baseTypeNameOrError = baseTypeError;
+}
+
+/*!
+\internal
+The name of the module is only saved in the QmlComponent. Iterate through the parent scopes until
+the QmlComponent or the root is reached to find out the module name of the component in which `this`
+resides.
+*/
+QString QQmlJSScope::moduleName() const
+{
+ for (const QQmlJSScope *it = this; it; it = it->parentScope().get()) {
+ const QString name = it->ownModuleName();
+ if (!name.isEmpty())
+ return name;
+ }
+ return {};
+}
+
+QString QQmlJSScope::baseTypeError() const
+{
+ return m_flags.testFlag(HasBaseTypeError) ? m_baseTypeNameOrError : QString();
+}
+
+QString QQmlJSScope::attachedTypeName() const
+{
+ QString name;
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
+ if (mode != QQmlJSScope::NotExtension)
+ return false;
+ if (scope->ownAttachedType().isNull())
+ return false;
+ name = scope->ownAttachedTypeName();
+ return true;
+ });
+
+ return name;
+}
+
+QQmlJSScope::ConstPtr QQmlJSScope::attachedType() const
+{
+ QQmlJSScope::ConstPtr ptr;
+ QQmlJSUtils::searchBaseAndExtensionTypes(
+ this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
+ if (mode != QQmlJSScope::NotExtension)
+ return false;
+ if (scope->ownAttachedType().isNull())
+ return false;
+ ptr = scope->ownAttachedType();
+ return true;
+ });
+
+ return ptr;
+}
+
+QQmlJSScope::AnnotatedScope QQmlJSScope::extensionType() const
+{
+ if (!m_extensionType)
+ return { m_extensionType, NotExtension };
+ if (m_flags & ExtensionIsJavaScript)
+ return { m_extensionType, ExtensionJavaScript };
+ if (m_flags & ExtensionIsNamespace)
+ return { m_extensionType, ExtensionNamespace };
+ return { m_extensionType, ExtensionType };
+}
+
+void QQmlJSScope::addOwnRuntimeFunctionIndex(QQmlJSMetaMethod::AbsoluteFunctionIndex index)
+{
+ m_runtimeFunctionIndices.emplaceBack(index);
+}
+
+bool QQmlJSScope::isResolved() const
+{
+ const bool nameIsEmpty = (m_scopeType == ScopeType::AttachedPropertyScope
+ || m_scopeType == ScopeType::GroupedPropertyScope)
+ ? m_internalName.isEmpty()
+ : m_baseTypeNameOrError.isEmpty();
+ if (nameIsEmpty)
+ return true;
+ if (m_baseType.scope.isNull())
+ return false;
+ if (isComposite() && !nonCompositeBaseType(baseType()))
+ return false;
+ return true;
+}
+
+QString QQmlJSScope::defaultPropertyName() const
+{
+ QString name;
+ QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
+ name = scope->ownDefaultPropertyName();
+ return !name.isEmpty();
+ });
+ return name;
+}
+
+QString QQmlJSScope::parentPropertyName() const
+{
+ QString name;
+ QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
+ name = scope->ownParentPropertyName();
+ return !name.isEmpty();
+ });
+ return name;
+}
+
+bool QQmlJSScope::isFullyResolved() const
+{
+ bool baseResolved = true;
+ QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
+ if (!scope->isResolved()) {
+ baseResolved = false;
+ return true;
+ }
+ return false;
+ });
+
+ return baseResolved;
+}
+
+QQmlJSScope::Export::Export(
+ QString package, QString type, QTypeRevision version, QTypeRevision revision)
+ : m_package(std::move(package))
+ , m_type(std::move(type))
+ , m_version(std::move(version))
+ , m_revision(std::move(revision))
{
}
@@ -376,13 +1143,226 @@ bool QQmlJSScope::Export::isValid() const
return m_version.isValid() || !m_package.isEmpty() || !m_type.isEmpty();
}
-QQmlJSScope QDeferredFactory<QQmlJSScope>::create() const
+void QDeferredFactory<QQmlJSScope>::populate(const QSharedPointer<QQmlJSScope> &scope) const
{
+ scope->setOwnModuleName(m_moduleName);
QQmlJSTypeReader typeReader(m_importer, m_filePath);
- QQmlJSScope::Ptr result = typeReader();
- m_importer->m_warnings.append(typeReader.errors());
- result->setInternalName(QFileInfo(m_filePath).baseName());
- return std::move(*result);
+ typeReader(scope);
+ m_importer->m_globalWarnings.append(typeReader.errors());
+ scope->setInternalName(internalName());
+ QQmlJSScope::resolveEnums(scope, m_importer->builtinInternalNames());
+ QQmlJSScope::resolveList(scope, m_importer->builtinInternalNames().arrayType());
+
+ if (m_isSingleton && !scope->isSingleton()) {
+ m_importer->m_globalWarnings.append(
+ { QStringLiteral(
+ "Type %1 declared as singleton in qmldir but missing pragma Singleton")
+ .arg(scope->internalName()),
+ QtCriticalMsg, QQmlJS::SourceLocation() });
+ scope->setIsSingleton(true);
+ } else if (!m_isSingleton && scope->isSingleton()) {
+ m_importer->m_globalWarnings.append(
+ { QStringLiteral("Type %1 not declared as singleton in qmldir "
+ "but using pragma Singleton")
+ .arg(scope->internalName()),
+ QtCriticalMsg, QQmlJS::SourceLocation() });
+ scope->setIsSingleton(false);
+ }
+}
+
+/*!
+ \internal
+ Checks whether \a derived type can be assigned to this type. Returns \c
+ true if the type hierarchy of \a derived contains a type equal to this.
+
+ \note Assigning \a derived to "QVariant" or "QJSValue" is always possible and
+ the function returns \c true in this case. In addition any "QObject" based \a derived type
+ can be assigned to a this type if that type is derived from "QQmlComponent".
+ */
+bool QQmlJSScope::canAssign(const QQmlJSScope::ConstPtr &derived) const
+{
+ if (!derived)
+ return false;
+
+ // expect this and derived types to have non-composite bases
+ Q_ASSERT(!isComposite() || nonCompositeBaseType(baseType()));
+ Q_ASSERT(nonCompositeBaseType(derived));
+
+ // the logic with isBaseComponent (as well as the way we set this flag)
+ // feels wrong - QTBUG-101940
+ const bool isBaseComponent = [this]() {
+ if (internalName() == u"QQmlComponent")
+ return true;
+ else if (isComposite())
+ return false;
+ for (auto cppBase = nonCompositeBaseType(baseType()); cppBase;
+ cppBase = cppBase->baseType()) {
+ if (cppBase->internalName() == u"QQmlAbstractDelegateComponent")
+ return true;
+ }
+ return false;
+ }();
+
+ QDuplicateTracker<QQmlJSScope::ConstPtr> seen;
+ for (auto scope = derived; !scope.isNull() && !seen.hasSeen(scope);
+ scope = scope->baseType()) {
+ if (isSameType(scope))
+ return true;
+ if (isBaseComponent && scope->internalName() == u"QObject"_s)
+ return true;
+ }
+
+ if (internalName() == u"QVariant"_s || internalName() == u"QJSValue"_s)
+ return true;
+
+ return isListProperty() && valueType()->canAssign(derived);
+}
+
+/*!
+ \internal
+ Checks whether this type or its parents have a custom parser.
+*/
+bool QQmlJSScope::isInCustomParserParent() const
+{
+ for (const auto *scope = this; scope; scope = scope->parentScope().get()) {
+ if (!scope->baseType().isNull() && scope->baseType()->hasCustomParser())
+ return true;
+ }
+
+ return false;
}
+/*!
+ * \internal
+ * if this->isInlineComponent(), then this getter returns the name of the inline
+ * component.
+ */
+std::optional<QString> QQmlJSScope::inlineComponentName() const
+{
+ Q_ASSERT(isInlineComponent() == m_inlineComponentName.has_value());
+ return m_inlineComponentName;
+}
+
+/*!
+ * \internal
+ * If this type is part of an inline component, return its name. Otherwise, if this type
+ * is part of the document root, return the document root name.
+ */
+QQmlJSScope::InlineComponentOrDocumentRootName QQmlJSScope::enclosingInlineComponentName() const
+{
+ for (auto *type = this; type; type = type->parentScope().get()) {
+ if (type->isInlineComponent())
+ return *type->inlineComponentName();
+ }
+ return RootDocumentNameType();
+}
+
+QVector<QQmlJSScope::ConstPtr> QQmlJSScope::childScopes() const
+{
+ QVector<QQmlJSScope::ConstPtr> result;
+ result.reserve(m_childScopes.size());
+ for (const auto &child : m_childScopes)
+ result.append(child);
+ return result;
+}
+
+/*!
+ \internal
+ Returns true if the current type is creatable by checking all the required base classes.
+ "Uncreatability" is only inherited from base types for composite types (in qml) and not for non-composite types (c++).
+
+For the exact definition:
+A type is uncreatable if and only if one of its composite base type or its first non-composite base type matches
+ following criteria:
+ \list
+ \li the base type is a singleton, or
+ \li the base type is an attached type, or
+ \li the base type is a C++ type with the QML_UNCREATABLE or QML_ANONYMOUS macro, or
+ \li the base type is a type without default constructor (in that case, it really needs QML_UNCREATABLE or QML_ANONYMOUS)
+ \endlist
+ */
+bool QQmlJSScope::isCreatable() const
+{
+ auto isCreatableNonRecursive = [](const QQmlJSScope *scope) {
+ return scope->hasCreatableFlag() && !scope->isSingleton()
+ && scope->scopeType() == QQmlSA::ScopeType::QMLScope;
+ };
+
+ for (const QQmlJSScope* scope = this; scope; scope = scope->baseType().get()) {
+ if (!scope->isComposite()) {
+ // just check the first nonComposite (c++) base for isCreatableNonRecursive() and then stop
+ return isCreatableNonRecursive(scope);
+ } else {
+ // check all composite (qml) bases for isCreatableNonRecursive().
+ if (isCreatableNonRecursive(scope))
+ return true;
+ }
+ }
+ // no uncreatable bases found
+ return false;
+}
+
+bool QQmlJSScope::isStructured() const
+{
+ for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
+ if (!scope->isComposite())
+ return scope->hasStructuredFlag();
+ }
+ return false;
+}
+
+QQmlSA::Element QQmlJSScope::createQQmlSAElement(const ConstPtr &ptr)
+{
+ QQmlSA::Element element;
+ *reinterpret_cast<QQmlJSScope::ConstPtr *>(element.m_data) = ptr;
+ return element;
+}
+
+QQmlSA::Element QQmlJSScope::createQQmlSAElement(ConstPtr &&ptr)
+{
+ QQmlSA::Element element;
+ *reinterpret_cast<QQmlJSScope::ConstPtr *>(element.m_data) = std::move(ptr);
+ return element;
+}
+
+const QQmlJSScope::ConstPtr &QQmlJSScope::scope(const QQmlSA::Element &element)
+{
+ return *reinterpret_cast<const QQmlJSScope::ConstPtr *>(element.m_data);
+}
+
+QTypeRevision
+QQmlJSScope::nonCompositeBaseRevision(const ImportedScope<QQmlJSScope::ConstPtr> &scope)
+{
+ for (auto base = scope; base.scope;
+ base = { base.scope->m_baseType.scope, base.scope->m_baseType.revision }) {
+ if (!base.scope->isComposite())
+ return base.revision;
+ }
+ return {};
+}
+
+/*!
+ \internal
+ Checks whether \a otherScope is the same type as this.
+
+ In addition to checking whether the scopes are identical, we also cover duplicate scopes with
+ the same internal name.
+ */
+bool QQmlJSScope::isSameType(const ConstPtr &otherScope) const
+{
+ return this == otherScope.get()
+ || (!this->internalName().isEmpty()
+ && this->internalName() == otherScope->internalName());
+}
+
+bool QQmlJSScope::inherits(const ConstPtr &base) const
+{
+ for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
+ if (scope->isSameType(base))
+ return true;
+ }
+ return false;
+}
+
+
QT_END_NAMESPACE