Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmljsscope.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:significant
4
10#include "qqmlsa.h"
11
12#include <QtCore/qqueue.h>
13#include <QtCore/qsharedpointer.h>
14
15#include <private/qduplicatetracker_p.h>
16
17#include <algorithm>
18
19QT_BEGIN_NAMESPACE
20
21/*!
22 \class QQmlJSScope
23 \internal
24 \brief Tracks the types for the QmlCompiler
25
26 QQmlJSScope tracks the types used in qml for the QmlCompiler.
27
28 Multiple QQmlJSScope objects might be created for the same conceptual type, except when reused
29 due to extensive caching. Two QQmlJSScope objects are considered equal when they are backed
30 by the same implementation, that is, they have the same internalName.
31*/
32
33using namespace Qt::StringLiterals;
34
35QQmlJSScope::QQmlJSScope(const QString &internalName) : QQmlJSScope{}
36{
37 m_internalName = internalName;
38}
39
40void QQmlJSScope::reparent(const QQmlJSScope::Ptr &parentScope, const QQmlJSScope::Ptr &childScope)
41{
42 if (const QQmlJSScope::Ptr parent = childScope->m_parentScope.toStrongRef())
43 parent->m_childScopes.removeOne(childScope);
44 if (parentScope)
45 parentScope->m_childScopes.append(childScope);
46 childScope->m_parentScope = parentScope;
47}
48
49QQmlJSScope::Ptr QQmlJSScope::clone(const ConstPtr &origin)
50{
51 if (origin.isNull())
52 return QQmlJSScope::Ptr();
53 QQmlJSScope::Ptr cloned = create();
54 *cloned = *origin;
55 if (QQmlJSScope::Ptr parent = cloned->parentScope())
56 parent->m_childScopes.append(cloned);
57 return cloned;
58}
59
60/*!
61\internal
62Return all the JavaScript identifiers defined in the current scope.
63*/
64QHash<QString, QQmlJSScope::JavaScriptIdentifier> QQmlJSScope::ownJSIdentifiers() const
65{
66 return m_jsIdentifiers;
67}
68
69void QQmlJSScope::insertJSIdentifier(const QString &name, const JavaScriptIdentifier &identifier)
70{
71 Q_ASSERT(m_scopeType != QQmlSA::ScopeType::QMLScope);
72 if (identifier.kind == JavaScriptIdentifier::LexicalScoped
73 || identifier.kind == JavaScriptIdentifier::Injected
74 || QQmlSA::isFunctionScope(m_scopeType)) {
75 m_jsIdentifiers.insert(name, identifier);
76 } else {
77 auto targetScope = parentScope();
78 while (targetScope->m_scopeType != QQmlSA::ScopeType::JSFunctionScope)
79 targetScope = targetScope->parentScope();
80 targetScope->m_jsIdentifiers.insert(name, identifier);
81 }
82}
83
84void QQmlJSScope::setLineNumber(quint32 lineNumber)
85{
86 m_sourceLocation.startLine = lineNumber;
87 // also set the startColumn to make the QQmlJSSourceLocation usable
88 m_sourceLocation.startColumn = 1;
89}
90
91void QQmlJSScope::insertPropertyIdentifier(const QQmlJSMetaProperty &property)
92{
93 addOwnProperty(property);
94 QQmlJSMetaMethod method(
95 QQmlSignalNames::propertyNameToChangedSignalName(property.propertyName()), u"void"_s);
96 method.setMethodType(QQmlJSMetaMethodType::Signal);
97 method.setIsImplicitQmlPropertyChangeSignal(true);
98 addOwnMethod(method);
99}
100
101bool QQmlJSScope::hasMethod(const QString &name) const
102{
103 return QQmlJSUtils::searchBaseAndExtensionTypes(
104 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
105 if (mode == QQmlJSScope::ExtensionNamespace)
106 return false;
107 return scope->m_methods.contains(name);
108 });
109}
110
111/*!
112 Returns all methods visible from this scope including those of
113 base types and extensions.
114
115 \note Methods that get shadowed are not included and only the
116 version visible from this scope is contained. Additionally method
117 overrides are not included either, only the first visible version
118 of any method is included.
119*/
120QHash<QString, QQmlJSMetaMethod> QQmlJSScope::methods() const
121{
122 QHash<QString, QQmlJSMetaMethod> results;
123 QQmlJSUtils::searchBaseAndExtensionTypes(
124 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
125 if (mode == QQmlJSScope::ExtensionNamespace)
126 return false;
127 for (auto it = scope->m_methods.constBegin(); it != scope->m_methods.constEnd();
128 it++) {
129 if (!results.contains(it.key()))
130 results.insert(it.key(), it.value());
131 }
132 return false;
133 });
134
135 return results;
136}
137
138QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name) const
139{
140 QList<QQmlJSMetaMethod> results;
141
142 QQmlJSUtils::searchBaseAndExtensionTypes(
143 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
144 if (mode == QQmlJSScope::ExtensionNamespace)
145 return false;
146 results.append(scope->ownMethods(name));
147 return false;
148 });
149 return results;
150}
151
152QList<QQmlJSMetaMethod> QQmlJSScope::methods(const QString &name, QQmlJSMetaMethodType type) const
153{
154 QList<QQmlJSMetaMethod> results;
155
156 QQmlJSUtils::searchBaseAndExtensionTypes(
157 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
158 if (mode == QQmlJSScope::ExtensionNamespace)
159 return false;
160 const auto ownMethods = scope->ownMethods(name);
161 for (const auto &method : ownMethods) {
162 if (method.methodType() == type)
163 results.append(method);
164 }
165 return false;
166 });
167 return results;
168}
169
170bool QQmlJSScope::hasEnumeration(const QString &name) const
171{
172 return QQmlJSUtils::searchBaseAndExtensionTypes(
173 this, [&](const QQmlJSScope *scope) { return scope->m_enumerations.contains(name); });
174}
175
176bool QQmlJSScope::hasOwnEnumerationKey(const QString &name) const
177{
178 for (const auto &e : m_enumerations) {
179 if (e.keys().contains(name))
180 return true;
181 }
182 return false;
183}
184
185bool QQmlJSScope::hasEnumerationKey(const QString &name) const
186{
187 return QQmlJSUtils::searchBaseAndExtensionTypes(
188 this, [&](const QQmlJSScope *scope) { return scope->hasOwnEnumerationKey(name); });
189}
190
191QQmlJSMetaEnum QQmlJSScope::enumeration(const QString &name) const
192{
193 QQmlJSMetaEnum result;
194
195 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
196 const auto it = scope->m_enumerations.find(name);
197 if (it == scope->m_enumerations.end())
198 return false;
199 result = *it;
200 return true;
201 });
202
203 return result;
204}
205
206QHash<QString, QQmlJSMetaEnum> QQmlJSScope::enumerations() const
207{
208 QHash<QString, QQmlJSMetaEnum> results;
209
210 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
211 for (auto it = scope->m_enumerations.constBegin(); it != scope->m_enumerations.constEnd();
212 it++) {
213 if (!results.contains(it.key()))
214 results.insert(it.key(), it.value());
215 }
216 return false;
217 });
218
219 return results;
220}
221
222QString QQmlJSScope::augmentedInternalName() const
223{
224 using namespace Qt::StringLiterals;
225 Q_ASSERT(!m_internalName.isEmpty());
226
227 switch (m_semantics) {
228 case AccessSemantics::Reference:
229 return m_internalName + " *"_L1;
230 case AccessSemantics::Value:
231 case AccessSemantics::Sequence:
232 break;
233 case AccessSemantics::None:
234 // If we got a namespace, it might still be a regular type, exposed as namespace.
235 // We may need to travel the inheritance chain all the way up to QObject to
236 // figure this out, since all other types may be exposed the same way.
237 for (QQmlJSScope::ConstPtr base = baseType(); base; base = base->baseType()) {
238 switch (base->accessSemantics()) {
239 case AccessSemantics::Reference:
240 return m_internalName + " *"_L1;
241 case AccessSemantics::Value:
242 case AccessSemantics::Sequence:
243 return m_internalName;
244 case AccessSemantics::None:
245 break;
246 }
247 }
248 break;
249 }
250 return m_internalName;
251}
252
253QString QQmlJSScope::prettyName(QAnyStringView name)
254{
255 const auto internal = "$internal$."_L1;
256 const QString anonymous = "$anonymous$."_L1;
257
258 QString pretty = name.toString();
259
260 if (pretty.startsWith(internal))
261 pretty = pretty.mid(internal.size());
262 else if (pretty.startsWith(anonymous))
263 pretty = pretty.mid(anonymous.size());
264
265 if (pretty == u"std::nullptr_t")
266 return u"null"_s;
267
268 if (pretty == u"void")
269 return u"undefined"_s;
270
271 return pretty;
272}
273
274/*!
275 \internal
276
277 Returns \c Yes if the scope is the outermost element of a separate Component. Either:
278 a, It is the root element of a QML document
279 b, It is an inline component
280 c, It has been implicitly wrapped, e.g. due to an assignment to a Component property
281 d, It is the first (and only) child of a Component
282
283 Returns \c No if we can clearly determine that this is not the case.
284 Returns \c Maybe if the scope is assigned to an unknown property. This may
285 or may not be a Component.
286
287 For visitors: This method should only be called after implicit components
288 are detected, that is, after QQmlJSImportVisitor::endVisit(UiProgram *)
289 was called.
290 */
291QQmlJSScope::IsComponentRoot QQmlJSScope::componentRootStatus() const {
292 if (m_flags.testAnyFlags(
293 Flags(WrappedInImplicitComponent | FileRootComponent | InlineComponent))) {
294 return IsComponentRoot::Yes;
295 }
296
297 // If the object is assigned to an unknown property, assume it's Component.
298 if (m_flags.testFlag(AssignedToUnknownProperty))
299 return IsComponentRoot::Maybe;
300
301 auto base = nonCompositeBaseType(parentScope()); // handles null parentScope()
302 if (!base)
303 return IsComponentRoot::No;
304 return base->internalName() == u"QQmlComponent"
305 ? IsComponentRoot::Yes
306 : IsComponentRoot::No;
307}
308
309std::optional<QQmlJSScope::JavaScriptIdentifier>
310QQmlJSScope::jsIdentifier(const QString &id) const
311{
312 for (const auto *scope = this; scope; scope = scope->parentScope().data()) {
313 if (QQmlSA::isFunctionScope(scope->m_scopeType)
314 || scope->m_scopeType == QQmlSA::ScopeType::JSLexicalScope) {
315 auto it = scope->m_jsIdentifiers.find(id);
316 if (it != scope->m_jsIdentifiers.end())
317 return *it;
318 }
319 }
320
321 return std::optional<JavaScriptIdentifier>{};
322}
323
324std::optional<QQmlJSScope::JavaScriptIdentifier> QQmlJSScope::ownJSIdentifier(const QString &id) const
325{
326 auto it = m_jsIdentifiers.find(id);
327 if (it != m_jsIdentifiers.end())
328 return *it;
329
330 return std::optional<JavaScriptIdentifier>{};
331}
332
334qFindInlineComponents(QStringView typeName, const QQmlJS::ContextualTypes &contextualTypes)
335{
336 const int separatorIndex = typeName.lastIndexOf(u'.');
337 // do not crash in typeName.sliced() when it starts or ends with an '.'.
338 if (separatorIndex < 1 || separatorIndex >= typeName.size() - 1)
339 return {};
340
341 const auto parentIt = contextualTypes.types().constFind(typeName.first(separatorIndex).toString());
342 if (parentIt == contextualTypes.types().constEnd())
343 return {};
344
345 auto inlineComponentParent = *parentIt;
346
347 // find the inline components using BFS, as inline components defined in childrens are also
348 // accessible from other qml documents. Same for inline components defined in a base class of
349 // the parent. Use BFS over DFS as the inline components are probably not deeply-nested.
350
351 QStringView inlineComponentName = typeName.sliced(separatorIndex + 1);
352 QQueue<QQmlJSScope::ConstPtr> candidatesForInlineComponents;
353 candidatesForInlineComponents.enqueue(inlineComponentParent.scope);
354 while (candidatesForInlineComponents.size()) {
355 QQmlJSScope::ConstPtr current = candidatesForInlineComponents.dequeue();
356 if (!current) // if some type was not resolved, ignore it instead of crashing
357 continue;
358 if (current->isInlineComponent() && current->inlineComponentName() == inlineComponentName) {
359 return { current, inlineComponentParent.revision };
360 }
361
362 // check alternatively the inline components at layer 1 in current and basetype, then at
363 // layer 2, etc...
364 const auto &childScopes = current->childScopes();
365 for (const auto &child : childScopes) {
366 if (child->scopeType() == QQmlSA::ScopeType::QMLScope)
367 candidatesForInlineComponents.enqueue(child);
368 }
369
370 if (const auto base = current->baseType())
371 candidatesForInlineComponents.enqueue(base);
372 }
373 return {};
374}
375
376/*! \internal
377 * Finds a type in contextualTypes with given name.
378 * If a type is found, then its name is inserted into usedTypes (when provided).
379 * If contextualTypes has mode INTERNAl, then namespace resolution for enums is
380 * done (eg for Qt::Alignment).
381 * If contextualTypes has mode QML, then inline component resolution is done
382 * ("qmlFileName.IC" is correctly resolved from qmlFileName).
383 */
384QQmlJSScope::ImportedScope<QQmlJSScope::ConstPtr> QQmlJSScope::findType(
385 const QString &name, const QQmlJS::ContextualTypes &contextualTypes,
386 QSet<QString> *usedTypes)
387{
388 const auto useType = [&]() {
389 if (usedTypes != nullptr)
390 usedTypes->insert(name);
391 };
392
393 auto type = contextualTypes.types().constFind(name);
394
395 if (type != contextualTypes.types().constEnd()) {
396 useType();
397 return *type;
398 }
399
400 const auto findListType = [&](const QString &prefix, const QString &postfix)
401 -> ImportedScope<ConstPtr> {
402 if (name.startsWith(prefix) && name.endsWith(postfix)) {
403 const qsizetype prefixLength = prefix.length();
404 const QString &elementName
405 = name.mid(prefixLength, name.length() - prefixLength - postfix.length());
406 const ImportedScope<ConstPtr> element
407 = findType(elementName, contextualTypes, usedTypes);
408 if (element.scope) {
409 useType();
410 return { element.scope->listType(), element.revision };
411 }
412 }
413
414 return {};
415 };
416
417 switch (contextualTypes.context()) {
418 case QQmlJS::ContextualTypes::INTERNAL: {
419 if (const auto listType = findListType(u"QList<"_s, u">"_s);
420 listType.scope && !listType.scope->isReferenceType()) {
421 return listType;
422 }
423
424 if (const auto listType = findListType(u"QQmlListProperty<"_s, u">"_s);
425 listType.scope && listType.scope->isReferenceType()) {
426 return listType;
427 }
428
429 // look for c++ namescoped enums!
430 const auto colonColon = name.lastIndexOf(QStringLiteral("::"));
431 if (colonColon == -1)
432 break;
433
434 const QString outerTypeName = name.left(colonColon);
435 const auto outerType = contextualTypes.types().constFind(outerTypeName);
436 if (outerType == contextualTypes.types().constEnd())
437 break;
438
439 for (const auto &innerType : std::as_const(outerType->scope->m_childScopes)) {
440 if (innerType->m_internalName == name) {
441 useType();
442 return { innerType, outerType->revision };
443 }
444 }
445
446 break;
447 }
448 case QQmlJS::ContextualTypes::QML: {
449 // look after inline components
450 const auto inlineComponent = qFindInlineComponents(name, contextualTypes);
451 if (inlineComponent.scope) {
452 useType();
453 return inlineComponent;
454 }
455
456 if (const auto listType = findListType(u"list<"_s, u">"_s); listType.scope)
457 return listType;
458
459 break;
460 }
461 }
462 return {};
463}
464
465QTypeRevision QQmlJSScope::resolveType(
466 const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &context,
467 QSet<QString> *usedTypes)
468{
469 if (self->accessSemantics() == AccessSemantics::Sequence
470 && self->internalName().startsWith(u"QQmlListProperty<"_s)) {
471 self->setIsListProperty(true);
472 }
473
474 const QString baseTypeName = self->baseTypeName();
475 const auto baseType = findType(baseTypeName, context, usedTypes);
476 if (!self->m_baseType.scope && !baseTypeName.isEmpty())
477 self->m_baseType = { baseType.scope, baseType.revision };
478
479 if (!self->m_attachedType && !self->m_attachedTypeName.isEmpty())
480 self->m_attachedType = findType(self->m_attachedTypeName, context, usedTypes).scope;
481
482 if (!self->m_elementType && !self->m_elementTypeName.isEmpty())
483 self->m_elementType = findType(self->m_elementTypeName, context, usedTypes).scope;
484
485 if (!self->m_extensionType) {
486 if (self->m_extensionTypeName.isEmpty()) {
487 if (self->accessSemantics() == AccessSemantics::Sequence) {
488 // All sequence types are implicitly extended by JS Array.
489 self->setExtensionTypeName(u"Array"_s);
490 self->setExtensionIsJavaScript(true);
491 self->m_extensionType = context.arrayType();
492 }
493 } else {
494 self->m_extensionType = findType(self->m_extensionTypeName, context, usedTypes).scope;
495 }
496 }
497
498
499 for (auto it = self->m_properties.begin(), end = self->m_properties.end(); it != end; ++it) {
500 const QString typeName = it->typeName();
501 if (it->type() || typeName.isEmpty())
502 continue;
503
504 if (const auto type = findType(typeName, context, usedTypes); type.scope) {
505 it->setType(it->isList() ? type.scope->listType() : type.scope);
506 continue;
507 }
508
509 const auto enumeration = self->m_enumerations.find(typeName);
510 if (enumeration != self->m_enumerations.end()) {
511 it->setType(it->isList()
512 ? enumeration->type()->listType()
513 : QQmlJSScope::ConstPtr(enumeration->type()));
514 }
515 }
516
517 const auto resolveParameter = [&](QQmlJSMetaParameter &parameter) {
518 if (const QString typeName = parameter.typeName();
519 !parameter.type() && !typeName.isEmpty()) {
520 auto type = findType(typeName, context, usedTypes);
521 if (type.scope && parameter.isList()) {
522 type.scope = type.scope->listType();
523 parameter.setIsList(false);
524 parameter.setIsPointer(false);
525 parameter.setTypeName(type.scope ? type.scope->internalName() : QString());
526 } else if (type.scope && type.scope->isReferenceType()) {
527 parameter.setIsPointer(true);
528 }
529 parameter.setType({ type.scope });
530 }
531 };
532
533 for (auto it = self->m_methods.begin(), end = self->m_methods.end(); it != end; ++it) {
534 auto returnValue = it->returnValue();
535 resolveParameter(returnValue);
536 it->setReturnValue(returnValue);
537
538 auto parameters = it->parameters();
539 for (int i = 0, length = parameters.size(); i < length; ++i)
540 resolveParameter(parameters[i]);
541 it->setParameters(parameters);
542 }
543
544 for (auto it = self->m_jsIdentifiers.begin(); it != self->m_jsIdentifiers.end(); ++it) {
545 if (it->typeName)
546 it->scope = findType(it->typeName.value(), context, usedTypes).scope;
547 }
548
549 return baseType.revision;
550}
551
552void QQmlJSScope::updateChildScope(
553 const QQmlJSScope::Ptr &childScope, const QQmlJSScope::Ptr &self,
554 const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
555{
556 switch (childScope->scopeType()) {
557 case QQmlSA::ScopeType::GroupedPropertyScope:
558 QQmlJSUtils::searchBaseAndExtensionTypes(
559 self.data(), [&](const QQmlJSScope *type, QQmlJSScope::ExtensionKind mode) {
560 if (mode == QQmlJSScope::ExtensionNamespace)
561 return false;
562 const auto propertyIt = type->m_properties.find(childScope->internalName());
563 if (propertyIt != type->m_properties.end()) {
564 childScope->m_baseType.scope = QQmlJSScope::ConstPtr(propertyIt->type());
565 if (propertyIt->type())
566 childScope->m_semantics = propertyIt->type()->accessSemantics();
567 childScope->setBaseTypeName(propertyIt->typeName());
568 return true;
569 }
570 return false;
571 });
572 break;
573 case QQmlSA::ScopeType::AttachedPropertyScope:
574 if (const auto attachedBase = findType(
575 childScope->internalName(), contextualTypes, usedTypes).scope) {
576 childScope->m_baseType.scope = attachedBase->attachedType();
577 childScope->setBaseTypeName(attachedBase->attachedTypeName());
578 }
579 break;
580 default:
581 break;
582 }
583}
584
585template<typename Resolver, typename ChildScopeUpdater>
587 Resolver resolve, ChildScopeUpdater update, const QQmlJSScope::Ptr &self,
588 const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
589{
590 const QTypeRevision revision = resolve(self, contextualTypes, usedTypes);
591 // NB: constness ensures no detach
592 const auto childScopes = self->childScopes();
593 for (auto it = childScopes.begin(), end = childScopes.end(); it != end; ++it) {
594 const auto childScope = *it;
595 update(childScope, self, contextualTypes, usedTypes);
596 resolveTypesInternal(resolve, update, childScope, contextualTypes, usedTypes); // recursion
597 }
598 return revision;
599}
600
601QTypeRevision QQmlJSScope::resolveTypes(
602 const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
603 QSet<QString> *usedTypes)
604{
605 const auto resolveAll = [](const QQmlJSScope::Ptr &self,
606 const QQmlJS::ContextualTypes &contextualTypes,
607 QSet<QString> *usedTypes) {
608 resolveEnums(self, contextualTypes, usedTypes);
609 resolveList(self, contextualTypes.arrayType());
610 return resolveType(self, contextualTypes, usedTypes);
611 };
612 return resolveTypesInternal(resolveAll, updateChildScope, self, contextualTypes, usedTypes);
613}
614
615void QQmlJSScope::resolveNonEnumTypes(
616 const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
617 QSet<QString> *usedTypes)
618{
619 resolveTypesInternal(resolveType, updateChildScope, self, contextualTypes, usedTypes);
620}
621
622static QString flagStorage(const QString &underlyingType)
623{
624 // All numeric types are builtins. Therefore we can exhaustively check the internal names.
625
626 if (underlyingType == u"uint"
627 || underlyingType == u"quint8"
628 || underlyingType == u"ushort"
629 || underlyingType == u"ulonglong") {
630 return u"uint"_s;
631 }
632
633 if (underlyingType == u"int"
634 || underlyingType == u"qint8"
635 || underlyingType == u"short"
636 || underlyingType == u"longlong") {
637 return u"int"_s;
638 }
639
640 // Will fail to resolve and produce an error on usage.
641 // It's harmless if you never use the enum.
642 return QString();
643}
644
645/*!
646 \internal
647 Resolves all enums of self.
648
649 Some enums happen to have an alias, e.g. when an enum is used as a flag, the enum will exist in
650 two versions, once as enum (e.g. Qt::MouseButton) and once as a flag (e.g. Qt::MouseButtons). In
651 this case, normally only the flag is exposed to the qt metatype system and tools like qmltc will
652 have troubles when encountering the enum in signal parameters etc. To solve this problem,
653 resolveEnums() will create a QQmlJSMetaEnum copy for the alias in case the 'self'-scope already
654 does not have an enum called like the alias.
655 */
656void QQmlJSScope::resolveEnums(
657 const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
658 QSet<QString> *usedTypes)
659{
660 // temporary hash to avoid messing up m_enumerations while iterators are active on it
661 QHash<QString, QQmlJSMetaEnum> toBeAppended;
662 for (auto it = self->m_enumerations.begin(), end = self->m_enumerations.end(); it != end; ++it) {
663 if (it->type())
664 continue;
665 QQmlJSScope::Ptr enumScope = QQmlJSScope::create();
666 reparent(self, enumScope);
667 enumScope->m_scopeType = QQmlSA::ScopeType::EnumScope;
668
669 QString typeName = it->typeName();
670 if (typeName.isEmpty())
671 typeName = QStringLiteral("int");
672 else if (it->isFlag())
673 typeName = flagStorage(typeName);
674 enumScope->setBaseTypeName(typeName);
675 const auto type = findType(typeName, contextualTypes, usedTypes);
676 enumScope->m_baseType = { type.scope, type.revision };
677
678 enumScope->m_semantics = AccessSemantics::Value;
679 enumScope->m_internalName = self->internalName() + QStringLiteral("::") + it->name();
680 resolveList(enumScope, contextualTypes.arrayType());
681 if (QString alias = it->alias(); !alias.isEmpty()
682 && self->m_enumerations.constFind(alias) == self->m_enumerations.constEnd()) {
683 auto aliasScope = QQmlJSScope::clone(enumScope);
684 aliasScope->m_internalName = self->internalName() + QStringLiteral("::") + alias;
685 QQmlJSMetaEnum cpy(*it);
686 cpy.setType(QQmlJSScope::ConstPtr(aliasScope));
687 toBeAppended.insert(alias, cpy);
688 }
689 it->setType(QQmlJSScope::ConstPtr(enumScope));
690 }
691 // no more iterators active on m_enumerations, so it can be changed safely now
692 self->m_enumerations.insert(toBeAppended);
693}
694
695void QQmlJSScope::resolveList(const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &arrayType)
696{
697 if (self->listType() || self->accessSemantics() == AccessSemantics::Sequence)
698 return;
699
700 Q_ASSERT(!arrayType.isNull());
701 QQmlJSScope::Ptr listType = QQmlJSScope::create();
702 listType->setAccessSemantics(AccessSemantics::Sequence);
703 listType->setElementTypeName(self->internalName());
704
705 if (self->isComposite()) {
706 // There is no internalName for this thing. Just set the value type right away
707 listType->setInternalName(u"QQmlListProperty<>"_s);
708 listType->m_elementType = QQmlJSScope::ConstPtr(self);
709 } else if (self->isReferenceType()) {
710 listType->setInternalName(u"QQmlListProperty<%2>"_s.arg(self->internalName()));
711 // Do not set a filePath on the list type, so that we have to generalize it
712 // even in direct mode.
713 } else {
714 listType->setInternalName(u"QList<%2>"_s.arg(self->internalName()));
715 listType->setFilePath(self->filePath());
716 }
717
718 const QQmlJSImportedScope element = {self, QTypeRevision()};
719 const QQmlJSImportedScope array = {arrayType, QTypeRevision()};
720 QQmlJS::ContextualTypes contextualTypes(
721 QQmlJS::ContextualTypes::INTERNAL,
722 { { self->internalName(), element }, },
723 { { self, self->internalName() }, },
724 arrayType);
725 QQmlJSScope::resolveTypes(listType, contextualTypes);
726
727 Q_ASSERT(listType->elementType() == self);
728 self->m_listType = listType;
729}
730
731void QQmlJSScope::resolveGroup(
732 const Ptr &self, const ConstPtr &baseType,
733 const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
734{
735 Q_ASSERT(baseType);
736 // Generalized group properties are always composite,
737 // which means we expect contextualTypes to be QML names.
738 Q_ASSERT(self->isComposite());
739
740 self->m_baseType.scope = baseType;
741 self->m_semantics = baseType->accessSemantics();
742 resolveNonEnumTypes(self, contextualTypes, usedTypes);
743}
744
745QQmlJSScope::ConstPtr QQmlJSScope::findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope)
746{
747 auto qmlScope = scope;
748 while (qmlScope && qmlScope->m_scopeType != QQmlSA::ScopeType::QMLScope)
749 qmlScope = qmlScope->parentScope();
750 return qmlScope;
751}
752
753bool QQmlJSScope::hasProperty(const QString &name) const
754{
755 return QQmlJSUtils::searchBaseAndExtensionTypes(
756 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
757 if (mode == QQmlJSScope::ExtensionNamespace)
758 return false;
759 return scope->m_properties.contains(name);
760 });
761}
762
763QQmlJSMetaProperty QQmlJSScope::property(const QString &name) const
764{
765 QQmlJSMetaProperty prop;
766 QQmlJSUtils::searchBaseAndExtensionTypes(
767 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
768 if (mode == QQmlJSScope::ExtensionNamespace)
769 return false;
770 const auto it = scope->m_properties.find(name);
771 if (it == scope->m_properties.end())
772 return false;
773 prop = *it;
774 return true;
775 });
776 return prop;
777}
778
779/*!
780 Returns all properties visible from this scope including those of
781 base types and extensions.
782
783 \note Properties that get shadowed are not included and only the
784 version visible from this scope is contained.
785*/
786QHash<QString, QQmlJSMetaProperty> QQmlJSScope::properties() const
787{
788 QHash<QString, QQmlJSMetaProperty> results;
789 QQmlJSUtils::searchBaseAndExtensionTypes(
790 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
791 if (mode == QQmlJSScope::ExtensionNamespace)
792 return false;
793 for (auto it = scope->m_properties.constBegin();
794 it != scope->m_properties.constEnd(); it++) {
795 if (!results.contains(it.key()))
796 results.insert(it.key(), it.value());
797 }
798 return false;
799 });
800 return results;
801}
802
803QQmlJSScope::AnnotatedScope QQmlJSScope::ownerOfProperty(const QQmlJSScope::ConstPtr &self,
804 const QString &name)
805{
806 QQmlJSScope::AnnotatedScope owner;
807 QQmlJSUtils::searchBaseAndExtensionTypes(
808 self, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
809 if (mode == QQmlJSScope::ExtensionNamespace)
810 return false;
811 if (scope->hasOwnProperty(name)) {
812 owner = { scope, mode };
813 return true;
814 }
815 return false;
816 });
817 return owner;
818}
819
820void QQmlJSScope::setPropertyLocallyRequired(const QString &name, bool isRequired)
821{
822 if (!isRequired)
823 m_requiredPropertyNames.removeOne(name);
824 else if (!m_requiredPropertyNames.contains(name))
825 m_requiredPropertyNames.append(name);
826}
827
828bool QQmlJSScope::isPropertyRequired(const QString &name) const
829{
830 bool isRequired = false;
831 QQmlJSUtils::searchBaseAndExtensionTypes(
832 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
833 if (scope->isPropertyLocallyRequired(name)) {
834 isRequired = true;
835 return true;
836 }
837
838 // the hasOwnProperty() below only makes sense if our scope is
839 // not an extension namespace
840 if (mode == QQmlJSScope::ExtensionNamespace)
841 return false;
842
843 // If it has a property of that name, and that is not required, then none of the
844 // base types matter. You cannot make a derived type's property required with
845 // a "required" specification in a base type.
846 return scope->hasOwnProperty(name);
847 });
848 return isRequired;
849}
850
851bool QQmlJSScope::isPropertyLocallyRequired(const QString &name) const
852{
853 return m_requiredPropertyNames.contains(name);
854}
855
856void QQmlJSScope::addOwnPropertyBinding(const QQmlJSMetaPropertyBinding &binding, BindingTargetSpecifier specifier)
857{
858 Q_ASSERT(binding.sourceLocation().isValid());
859 m_propertyBindings.insert(binding.propertyName(), binding);
860
861 // NB: insert() prepends \a binding to the list of bindings, but we need
862 // append, so rotate
863 using iter = typename QMultiHash<QString, QQmlJSMetaPropertyBinding>::iterator;
864 std::pair<iter, iter> r = m_propertyBindings.equal_range(binding.propertyName());
865 std::rotate(r.first, std::next(r.first), r.second);
866
867 // additionally store bindings in the QmlIR compatible order
868 addOwnPropertyBindingInQmlIROrder(binding, specifier);
869 Q_ASSERT(m_propertyBindings.size() == m_propertyBindingsArray.size());
870}
871
872void QQmlJSScope::addOwnPropertyBindingInQmlIROrder(const QQmlJSMetaPropertyBinding &binding,
873 BindingTargetSpecifier specifier)
874{
875 // the order:
876 // * ordinary bindings are prepended to the binding array
877 // * list bindings are properly ordered within each other, so basically
878 // prepended "in bulk"
879 // * bindings to default properties (which are not explicitly mentioned in
880 // binding expression) are inserted by source location's offset
881
882 static_assert(QTypeInfo<QQmlJSScope::QmlIRCompatibilityBindingData>::isRelocatable,
883 "We really want T to be relocatable as it improves QList<T> performance");
884
885 switch (specifier) {
886 case BindingTargetSpecifier::SimplePropertyTarget: {
887 m_propertyBindingsArray.emplaceFront(binding.propertyName(),
888 binding.sourceLocation().offset);
889 break;
890 }
891 case BindingTargetSpecifier::ListPropertyTarget: {
892 const auto bindingOnTheSameProperty =
893 [&](const QQmlJSScope::QmlIRCompatibilityBindingData &x) {
894 return x.propertyName == binding.propertyName();
895 };
896 // fake "prepend in bulk" by appending a list binding to the sequence of
897 // bindings to the same property. there's an implicit QML language
898 // guarantee that such sequence does not contain arbitrary in-between
899 // bindings that do not belong to the same list property
900 auto pos = std::find_if_not(m_propertyBindingsArray.begin(), m_propertyBindingsArray.end(),
901 bindingOnTheSameProperty);
902 Q_ASSERT(pos == m_propertyBindingsArray.begin()
903 || std::prev(pos)->propertyName == binding.propertyName());
904 m_propertyBindingsArray.emplace(pos, binding.propertyName(),
905 binding.sourceLocation().offset);
906 break;
907 }
908 case BindingTargetSpecifier::UnnamedPropertyTarget: {
909 // see QmlIR::PoolList<>::findSortedInsertionPoint()
910 const auto findInsertionPoint = [this](const QQmlJSMetaPropertyBinding &x) {
911 qsizetype pos = -1;
912 for (auto it = m_propertyBindingsArray.cbegin(); it != m_propertyBindingsArray.cend();
913 ++it) {
914 if (!(it->sourceLocationOffset <= x.sourceLocation().offset))
915 break;
916 ++pos;
917 }
918 return pos;
919 };
920
921 // see QmlIR::PoolList<>::insertAfter()
922 const auto insertAfter = [this](qsizetype pos, const QQmlJSMetaPropertyBinding &x) {
923 if (pos == -1) {
924 m_propertyBindingsArray.emplaceFront(x.propertyName(), x.sourceLocation().offset);
925 } else if (pos == m_propertyBindingsArray.size()) {
926 m_propertyBindingsArray.emplaceBack(x.propertyName(), x.sourceLocation().offset);
927 } else {
928 // since we insert *after*, use (pos + 1) as insertion point
929 m_propertyBindingsArray.emplace(pos + 1, x.propertyName(),
930 x.sourceLocation().offset);
931 }
932 };
933
934 const qsizetype insertionPos = findInsertionPoint(binding);
935 insertAfter(insertionPos, binding);
936 break;
937 }
938 default: {
939 Q_UNREACHABLE();
940 break;
941 }
942 }
943}
944
945QList<QQmlJSMetaPropertyBinding> QQmlJSScope::ownPropertyBindingsInQmlIROrder() const
946{
947 QList<QQmlJSMetaPropertyBinding> qmlIrOrdered;
948 qmlIrOrdered.reserve(m_propertyBindingsArray.size());
949
950 for (const auto &data : m_propertyBindingsArray) {
951 const auto [first, last] = m_propertyBindings.equal_range(data.propertyName);
952 Q_ASSERT(first != last);
953 auto binding = std::find_if(first, last, [&](const QQmlJSMetaPropertyBinding &x) {
954 return x.sourceLocation().offset == data.sourceLocationOffset;
955 });
956 Q_ASSERT(binding != last);
957 qmlIrOrdered.append(*binding);
958 }
959
960 return qmlIrOrdered;
961}
962
963bool QQmlJSScope::hasPropertyBindings(const QString &name) const
964{
965 return QQmlJSUtils::searchBaseAndExtensionTypes(
966 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
967 if (mode != QQmlJSScope::NotExtension) {
968 Q_ASSERT(!scope->hasOwnPropertyBindings(name));
969 return false;
970 }
971 return scope->hasOwnPropertyBindings(name);
972 });
973}
974
975QList<QQmlJSMetaPropertyBinding> QQmlJSScope::propertyBindings(const QString &name) const
976{
977 QList<QQmlJSMetaPropertyBinding> bindings;
978 QQmlJSUtils::searchBaseAndExtensionTypes(
979 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
980 if (mode != QQmlJSScope::NotExtension) {
981 Q_ASSERT(!scope->hasOwnPropertyBindings(name));
982 return false;
983 }
984 const auto range = scope->ownPropertyBindings(name);
985 for (auto it = range.first; it != range.second; ++it)
986 bindings.append(*it);
987 return false;
988 });
989 return bindings;
990}
991
992bool QQmlJSScope::hasInterface(const QString &name) const
993{
994 return QQmlJSUtils::searchBaseAndExtensionTypes(
995 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
996 if (mode != QQmlJSScope::NotExtension)
997 return false;
998 return scope->m_interfaceNames.contains(name);
999 });
1000}
1001
1002bool QQmlJSScope::isNameDeferred(const QString &name) const
1003{
1004 bool isDeferred = false;
1005
1006 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
1007 const QStringList immediate = scope->ownImmediateNames();
1008 if (!immediate.isEmpty()) {
1009 isDeferred = !immediate.contains(name);
1010 return true;
1011 }
1012 const QStringList deferred = scope->ownDeferredNames();
1013 if (!deferred.isEmpty()) {
1014 isDeferred = deferred.contains(name);
1015 return true;
1016 }
1017 return false;
1018 });
1019
1020 return isDeferred;
1021}
1022
1023void QQmlJSScope::setBaseTypeName(const QString &baseTypeName)
1024{
1025 m_flags.setFlag(HasBaseTypeError, false);
1026 m_baseTypeNameOrError = baseTypeName;
1027}
1028
1029QString QQmlJSScope::baseTypeName() const
1030{
1031 return m_flags.testFlag(HasBaseTypeError) ? QString() : m_baseTypeNameOrError;
1032}
1033
1034void QQmlJSScope::setBaseTypeError(const QString &baseTypeError)
1035{
1036 m_flags.setFlag(HasBaseTypeError);
1037 m_baseTypeNameOrError = baseTypeError;
1038}
1039
1040/*!
1041\internal
1042The name of the module is only saved in the QmlComponent. Iterate through the parent scopes until
1043the QmlComponent or the root is reached to find out the module name of the component in which `this`
1044resides.
1045*/
1046QString QQmlJSScope::moduleName() const
1047{
1048 for (const QQmlJSScope *it = this; it; it = it->parentScope().get()) {
1049 const QString name = it->ownModuleName();
1050 if (!name.isEmpty())
1051 return name;
1052 }
1053 return {};
1054}
1055
1056QString QQmlJSScope::baseTypeError() const
1057{
1058 return m_flags.testFlag(HasBaseTypeError) ? m_baseTypeNameOrError : QString();
1059}
1060
1061QString QQmlJSScope::attachedTypeName() const
1062{
1063 QString name;
1064 QQmlJSUtils::searchBaseAndExtensionTypes(
1065 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
1066 if (mode != QQmlJSScope::NotExtension)
1067 return false;
1068 if (scope->ownAttachedType().isNull())
1069 return false;
1070 name = scope->ownAttachedTypeName();
1071 return true;
1072 });
1073
1074 return name;
1075}
1076
1077QQmlJSScope::ConstPtr QQmlJSScope::attachedType() const
1078{
1079 QQmlJSScope::ConstPtr ptr;
1080 QQmlJSUtils::searchBaseAndExtensionTypes(
1081 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
1082 if (mode != QQmlJSScope::NotExtension)
1083 return false;
1084 if (scope->ownAttachedType().isNull())
1085 return false;
1086 ptr = scope->ownAttachedType();
1087 return true;
1088 });
1089
1090 return ptr;
1091}
1092
1093QQmlJSScope::AnnotatedScope QQmlJSScope::extensionType() const
1094{
1095 if (!m_extensionType)
1096 return { m_extensionType, NotExtension };
1097 if (m_flags & ExtensionIsJavaScript)
1098 return { m_extensionType, ExtensionJavaScript };
1099 if (m_flags & ExtensionIsNamespace)
1100 return { m_extensionType, ExtensionNamespace };
1101 return { m_extensionType, ExtensionType };
1102}
1103
1104void QQmlJSScope::addOwnRuntimeFunctionIndex(QQmlJSMetaMethod::AbsoluteFunctionIndex index)
1105{
1106 m_runtimeFunctionIndices.emplaceBack(index);
1107}
1108
1109bool QQmlJSScope::isResolved() const
1110{
1111 const bool nameIsEmpty = (m_scopeType == ScopeType::AttachedPropertyScope
1112 || m_scopeType == ScopeType::GroupedPropertyScope)
1113 ? m_internalName.isEmpty()
1114 : m_baseTypeNameOrError.isEmpty();
1115 if (nameIsEmpty)
1116 return true;
1117 if (m_baseType.scope.isNull())
1118 return false;
1119 if (isComposite() && !nonCompositeBaseType(baseType()))
1120 return false;
1121 return true;
1122}
1123
1124QString QQmlJSScope::defaultPropertyName() const
1125{
1126 QString name;
1127 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
1128 name = scope->ownDefaultPropertyName();
1129 return !name.isEmpty();
1130 });
1131 return name;
1132}
1133
1134QString QQmlJSScope::parentPropertyName() const
1135{
1136 QString name;
1137 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
1138 name = scope->ownParentPropertyName();
1139 return !name.isEmpty();
1140 });
1141 return name;
1142}
1143
1144bool QQmlJSScope::isFullyResolved() const
1145{
1146 bool baseResolved = true;
1147 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
1148 if (!scope->isResolved()) {
1149 baseResolved = false;
1150 return true;
1151 }
1152 return false;
1153 });
1154
1155 return baseResolved;
1156}
1157
1158QQmlJSScope::Export::Export(
1159 QString package, QString type, QTypeRevision version, QTypeRevision revision)
1160 : m_package(std::move(package))
1161 , m_type(std::move(type))
1162 , m_version(std::move(version))
1163 , m_revision(std::move(revision))
1164{
1165}
1166
1167bool QQmlJSScope::Export::isValid() const
1168{
1169 return m_version.isValid() || !m_package.isEmpty() || !m_type.isEmpty();
1170}
1171
1172QDeferredFactory<QQmlJSScope>::QDeferredFactory(QQmlJSImporter *importer, const QString &filePath,
1173 const TypeReader &typeReader)
1174 : m_filePath(filePath),
1175 m_importer(importer),
1176 m_typeReader(typeReader ? typeReader
1177 : [](QQmlJSImporter *importer, const QString &filePath,
1178 const QSharedPointer<QQmlJSScope> &scopeToPopulate) {
1179 QQmlJSTypeReader defaultTypeReader(importer, filePath);
1180 defaultTypeReader(scopeToPopulate);
1181 return defaultTypeReader.errors();
1182 })
1183{
1184}
1185
1186void QDeferredFactory<QQmlJSScope>::populate(const QSharedPointer<QQmlJSScope> &scope) const
1187{
1188 scope->setOwnModuleName(m_moduleName);
1189 scope->setFilePath(m_filePath);
1190
1191 QList<QQmlJS::DiagnosticMessage> errors = m_typeReader(m_importer, m_filePath, scope);
1192 m_importer->m_globalWarnings.append(errors);
1193
1194 scope->setInternalName(internalName());
1195 QQmlJSScope::resolveEnums(
1196 scope, m_importer->builtinInternalNames().contextualTypes());
1197 QQmlJSScope::resolveList(
1198 scope, m_importer->builtinInternalNames().contextualTypes().arrayType());
1199
1200 if (m_isSingleton && !scope->isSingleton()) {
1201 m_importer->m_globalWarnings.append(
1202 { QStringLiteral(
1203 "Type %1 declared as singleton in qmldir but missing pragma Singleton")
1204 .arg(scope->internalName()),
1205 QtCriticalMsg, QQmlJS::SourceLocation() });
1206 scope->setIsSingleton(true);
1207 } else if (!m_isSingleton && scope->isSingleton()) {
1208 m_importer->m_globalWarnings.append(
1209 { QStringLiteral("Type %1 not declared as singleton in qmldir "
1210 "but using pragma Singleton")
1211 .arg(scope->internalName()),
1212 QtCriticalMsg, QQmlJS::SourceLocation() });
1213 scope->setIsSingleton(false);
1214 }
1215}
1216
1217/*!
1218 \internal
1219 Checks whether \a derived type can be assigned to this type. Returns \c
1220 true if the type hierarchy of \a derived contains a type equal to this.
1221
1222 \note Assigning \a derived to "QVariant" or "QJSValue" is always possible and
1223 the function returns \c true in this case. In addition any "QObject" based \a derived type
1224 can be assigned to a this type if that type is derived from "QQmlComponent".
1225 */
1226bool QQmlJSScope::canAssign(const QQmlJSScope::ConstPtr &derived) const
1227{
1228 if (!derived)
1229 return false;
1230
1231 // expect this and derived types to have non-composite bases
1232 Q_ASSERT(!isComposite() || nonCompositeBaseType(baseType()));
1233 Q_ASSERT(nonCompositeBaseType(derived));
1234
1235 // the logic with isBaseComponent (as well as the way we set this flag)
1236 // feels wrong - QTBUG-101940
1237 const bool isBaseComponent = [this]() {
1238 if (internalName() == u"QQmlComponent")
1239 return true;
1240 else if (isComposite())
1241 return false;
1242 for (auto cppBase = nonCompositeBaseType(baseType()); cppBase;
1243 cppBase = cppBase->baseType()) {
1244 if (cppBase->internalName() == u"QQmlAbstractDelegateComponent")
1245 return true;
1246 }
1247 return false;
1248 }();
1249
1250 QDuplicateTracker<QQmlJSScope::ConstPtr> seen;
1251 for (auto scope = derived; !scope.isNull() && !seen.hasSeen(scope);
1252 scope = scope->baseType()) {
1253 if (isSameType(scope))
1254 return true;
1255 if (isBaseComponent && scope->internalName() == u"QObject"_s)
1256 return true;
1257 }
1258
1259 if (internalName() == u"QVariant"_s || internalName() == u"QJSValue"_s)
1260 return true;
1261
1262 return isListProperty() && elementType()->canAssign(derived);
1263}
1264
1265/*!
1266 \internal
1267 Checks whether this type or its parents have a custom parser.
1268*/
1269bool QQmlJSScope::isInCustomParserParent() const
1270{
1271 for (const auto *scope = this; scope; scope = scope->parentScope().get()) {
1272 if (!scope->baseType().isNull() && scope->baseType()->hasCustomParser())
1273 return true;
1274 }
1275
1276 return false;
1277}
1278
1279/*!
1280 * \internal
1281 * if this->isInlineComponent(), then this getter returns the name of the inline
1282 * component.
1283 */
1284std::optional<QString> QQmlJSScope::inlineComponentName() const
1285{
1286 Q_ASSERT(isInlineComponent() == m_inlineComponentName.has_value());
1287 return m_inlineComponentName;
1288}
1289
1290/*!
1291 * \internal
1292 * If this type is part of an inline component, return its name. Otherwise, if this type
1293 * is part of the document root, return the document root name.
1294 */
1295QQmlJSScope::InlineComponentOrDocumentRootName QQmlJSScope::enclosingInlineComponentName() const
1296{
1297 for (auto *type = this; type; type = type->parentScope().get()) {
1298 if (type->isInlineComponent())
1299 return *type->inlineComponentName();
1300 }
1301 return RootDocumentNameType();
1302}
1303
1304QList<QQmlJSScope::ConstPtr> QQmlJSScope::childScopes() const
1305{
1306 QList<QQmlJSScope::ConstPtr> result;
1307 result.reserve(m_childScopes.size());
1308 for (const auto &child : m_childScopes)
1309 result.append(child);
1310 return result;
1311}
1312
1313/*!
1314 \internal
1315
1316 Returns true if this type or any base type of it has the "EnforcesScopedEnums" flag.
1317 The rationale is that you can turn on enforcement of scoped enums, but you cannot turn
1318 it off explicitly.
1319 */
1320bool QQmlJSScope::enforcesScopedEnums() const
1321{
1322 for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
1323 if (scope->hasEnforcesScopedEnumsFlag())
1324 return true;
1325 }
1326 return false;
1327}
1328
1329/*!
1330 \internal
1331 Returns true if the current type is creatable by checking all the required base classes.
1332 "Uncreatability" is only inherited from base types for composite types (in qml) and not for non-composite types (c++).
1333
1334For the exact definition:
1335A type is uncreatable if and only if one of its composite base type or its first non-composite base type matches
1336 following criteria:
1337 \list
1338 \li the base type is a singleton, or
1339 \li the base type is an attached type, or
1340 \li the base type is a C++ type with the QML_UNCREATABLE or QML_ANONYMOUS macro, or
1341 \li the base type is a type without default constructor (in that case, it really needs QML_UNCREATABLE or QML_ANONYMOUS)
1342 \endlist
1343 */
1344bool QQmlJSScope::isCreatable() const
1345{
1346 auto isCreatableNonRecursive = [](const QQmlJSScope *scope) {
1347 return scope->hasCreatableFlag() && !scope->isSingleton()
1348 && scope->scopeType() == QQmlSA::ScopeType::QMLScope;
1349 };
1350
1351 for (const QQmlJSScope* scope = this; scope; scope = scope->baseType().get()) {
1352 if (!scope->isComposite()) {
1353 // just check the first nonComposite (c++) base for isCreatableNonRecursive() and then stop
1354 return isCreatableNonRecursive(scope);
1355 } else {
1356 // check all composite (qml) bases for isCreatableNonRecursive().
1357 if (isCreatableNonRecursive(scope))
1358 return true;
1359 }
1360 }
1361 // no uncreatable bases found
1362 return false;
1363}
1364
1365bool QQmlJSScope::isStructured() const
1366{
1367 for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
1368 if (!scope->isComposite())
1369 return scope->hasStructuredFlag();
1370 }
1371 return false;
1372}
1373
1374QQmlSA::Element QQmlJSScope::createQQmlSAElement(const ConstPtr &ptr)
1375{
1376 QQmlSA::Element element;
1377 *reinterpret_cast<QQmlJSScope::ConstPtr *>(element.m_data) = ptr;
1378 return element;
1379}
1380
1381QQmlSA::Element QQmlJSScope::createQQmlSAElement(ConstPtr &&ptr)
1382{
1383 QQmlSA::Element element;
1384 *reinterpret_cast<QQmlJSScope::ConstPtr *>(element.m_data) = std::move(ptr);
1385 return element;
1386}
1387
1388const QQmlJSScope::ConstPtr &QQmlJSScope::scope(const QQmlSA::Element &element)
1389{
1390 return *reinterpret_cast<const QQmlJSScope::ConstPtr *>(element.m_data);
1391}
1392
1393QTypeRevision
1394QQmlJSScope::nonCompositeBaseRevision(const ImportedScope<QQmlJSScope::ConstPtr> &scope)
1395{
1396 for (auto base = scope; base.scope;
1397 base = { base.scope->m_baseType.scope, base.scope->m_baseType.revision }) {
1398 if (!base.scope->isComposite())
1399 return base.revision;
1400 }
1401 return {};
1402}
1403
1404/*!
1405 \internal
1406 Checks whether \a otherScope is the same type as this.
1407
1408 In addition to checking whether the scopes are identical, we also cover duplicate scopes with
1409 the same internal name.
1410 */
1411bool QQmlJSScope::isSameType(const ConstPtr &otherScope) const
1412{
1413 return this == otherScope.get()
1414 || (!this->internalName().isEmpty()
1415 && this->internalName() == otherScope->internalName());
1416}
1417
1418bool QQmlJSScope::inherits(const ConstPtr &base) const
1419{
1420 for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
1421 if (scope->isSameType(base))
1422 return true;
1423 }
1424 return false;
1425}
1426
1427
1428QT_END_NAMESPACE
static QQmlJSScope::ImportedScope< QQmlJSScope::ConstPtr > qFindInlineComponents(QStringView typeName, const QQmlJS::ContextualTypes &contextualTypes)
static QString flagStorage(const QString &underlyingType)
static QTypeRevision resolveTypesInternal(Resolver resolve, ChildScopeUpdater update, const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes, QSet< QString > *usedTypes)