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 QQmlJS::ContextualType element = { self, QTypeRevision(),
719 QQmlJS::PrecedenceValues::Default };
720 const QQmlJSImportedScope array = {arrayType, QTypeRevision()};
721 QQmlJS::ContextualTypes contextualTypes(
722 QQmlJS::ContextualTypes::INTERNAL,
723 { { self->internalName(), element }, },
724 { { self, self->internalName() }, },
725 arrayType);
726 QQmlJSScope::resolveTypes(listType, contextualTypes);
727
728 Q_ASSERT(listType->elementType() == self);
729 self->m_listType = listType;
730}
731
732void QQmlJSScope::resolveGroup(
733 const Ptr &self, const ConstPtr &baseType,
734 const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
735{
736 Q_ASSERT(baseType);
737 // Generalized group properties are always composite,
738 // which means we expect contextualTypes to be QML names.
739 Q_ASSERT(self->isComposite());
740
741 self->m_baseType.scope = baseType;
742 self->m_semantics = baseType->accessSemantics();
743 resolveNonEnumTypes(self, contextualTypes, usedTypes);
744}
745
746QQmlJSScope::ConstPtr QQmlJSScope::findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope)
747{
748 auto qmlScope = scope;
749 while (qmlScope && qmlScope->m_scopeType != QQmlSA::ScopeType::QMLScope)
750 qmlScope = qmlScope->parentScope();
751 return qmlScope;
752}
753
754bool QQmlJSScope::hasProperty(const QString &name) const
755{
756 return QQmlJSUtils::searchBaseAndExtensionTypes(
757 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
758 if (mode == QQmlJSScope::ExtensionNamespace)
759 return false;
760 return scope->m_properties.contains(name);
761 });
762}
763
764QQmlJSMetaProperty QQmlJSScope::property(const QString &name) const
765{
766 QQmlJSMetaProperty prop;
767 QQmlJSUtils::searchBaseAndExtensionTypes(
768 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
769 if (mode == QQmlJSScope::ExtensionNamespace)
770 return false;
771 const auto it = scope->m_properties.find(name);
772 if (it == scope->m_properties.end())
773 return false;
774 prop = *it;
775 return true;
776 });
777 return prop;
778}
779
780/*!
781 Returns all properties visible from this scope including those of
782 base types and extensions.
783
784 \note Properties that get shadowed are not included and only the
785 version visible from this scope is contained.
786*/
787QHash<QString, QQmlJSMetaProperty> QQmlJSScope::properties() const
788{
789 QHash<QString, QQmlJSMetaProperty> results;
790 QQmlJSUtils::searchBaseAndExtensionTypes(
791 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
792 if (mode == QQmlJSScope::ExtensionNamespace)
793 return false;
794 for (auto it = scope->m_properties.constBegin();
795 it != scope->m_properties.constEnd(); it++) {
796 if (!results.contains(it.key()))
797 results.insert(it.key(), it.value());
798 }
799 return false;
800 });
801 return results;
802}
803
804template <typename Predicate>
805QQmlJSScope::AnnotatedScope searchOwner(const QQmlJSScope::ConstPtr &self, Predicate &&p)
806{
807 QQmlJSScope::AnnotatedScope owner;
808 QQmlJSUtils::searchBaseAndExtensionTypes(
809 self, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
810 if (mode == QQmlJSScope::ExtensionNamespace)
811 return false;
812 if (p(scope)) {
813 owner = { scope, mode };
814 return true;
815 }
816 return false;
817 });
818 return owner;
819}
820
821QQmlJSScope::AnnotatedScope QQmlJSScope::ownerOfProperty(const QQmlJSScope::ConstPtr &self,
822 const QString &name)
823{
824 return searchOwner(self, [&name](const QQmlJSScope::ConstPtr &scope) {
825 return scope->hasOwnProperty(name);
826 });
827}
828
829QQmlJSScope::AnnotatedScope QQmlJSScope::ownerOfMethod(const QQmlJSScope::ConstPtr &self,
830 const QString &name)
831{
832 return searchOwner(self, [&name](const QQmlJSScope::ConstPtr &scope) {
833 return scope->hasOwnMethod(name);
834 });
835}
836
837void QQmlJSScope::setPropertyLocallyRequired(const QString &name, bool isRequired)
838{
839 if (!isRequired)
840 m_requiredPropertyNames.removeOne(name);
841 else if (!m_requiredPropertyNames.contains(name))
842 m_requiredPropertyNames.append(name);
843}
844
845bool QQmlJSScope::isPropertyRequired(const QString &name) const
846{
847 bool isRequired = false;
848 QQmlJSUtils::searchBaseAndExtensionTypes(
849 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
850 if (scope->isPropertyLocallyRequired(name)) {
851 isRequired = true;
852 return true;
853 }
854
855 // the hasOwnProperty() below only makes sense if our scope is
856 // not an extension namespace
857 if (mode == QQmlJSScope::ExtensionNamespace)
858 return false;
859
860 // If it has a property of that name, and that is not required, then none of the
861 // base types matter. You cannot make a derived type's property required with
862 // a "required" specification in a base type.
863 return scope->hasOwnProperty(name);
864 });
865 return isRequired;
866}
867
868bool QQmlJSScope::isPropertyLocallyRequired(const QString &name) const
869{
870 return m_requiredPropertyNames.contains(name);
871}
872
873void QQmlJSScope::addOwnPropertyBinding(const QQmlJSMetaPropertyBinding &binding, BindingTargetSpecifier specifier)
874{
875 Q_ASSERT(binding.sourceLocation().isValid());
876 m_propertyBindings.insert(binding.propertyName(), binding);
877
878 // NB: insert() prepends \a binding to the list of bindings, but we need
879 // append, so rotate
880 using iter = typename QMultiHash<QString, QQmlJSMetaPropertyBinding>::iterator;
881 std::pair<iter, iter> r = m_propertyBindings.equal_range(binding.propertyName());
882 std::rotate(r.first, std::next(r.first), r.second);
883
884 // additionally store bindings in the QmlIR compatible order
885 addOwnPropertyBindingInQmlIROrder(binding, specifier);
886 Q_ASSERT(m_propertyBindings.size() == m_propertyBindingsArray.size());
887}
888
889void QQmlJSScope::addOwnPropertyBindingInQmlIROrder(const QQmlJSMetaPropertyBinding &binding,
890 BindingTargetSpecifier specifier)
891{
892 // the order:
893 // * ordinary bindings are prepended to the binding array
894 // * list bindings are properly ordered within each other, so basically
895 // prepended "in bulk"
896 // * bindings to default properties (which are not explicitly mentioned in
897 // binding expression) are inserted by source location's offset
898
899 static_assert(QTypeInfo<QQmlJSScope::QmlIRCompatibilityBindingData>::isRelocatable,
900 "We really want T to be relocatable as it improves QList<T> performance");
901
902 switch (specifier) {
903 case BindingTargetSpecifier::SimplePropertyTarget: {
904 m_propertyBindingsArray.emplaceFront(binding.propertyName(),
905 binding.sourceLocation().offset);
906 break;
907 }
908 case BindingTargetSpecifier::ListPropertyTarget: {
909 const auto bindingOnTheSameProperty =
910 [&](const QQmlJSScope::QmlIRCompatibilityBindingData &x) {
911 return x.propertyName == binding.propertyName();
912 };
913 // fake "prepend in bulk" by appending a list binding to the sequence of
914 // bindings to the same property. there's an implicit QML language
915 // guarantee that such sequence does not contain arbitrary in-between
916 // bindings that do not belong to the same list property
917 auto pos = std::find_if_not(m_propertyBindingsArray.begin(), m_propertyBindingsArray.end(),
918 bindingOnTheSameProperty);
919 Q_ASSERT(pos == m_propertyBindingsArray.begin()
920 || std::prev(pos)->propertyName == binding.propertyName());
921 m_propertyBindingsArray.emplace(pos, binding.propertyName(),
922 binding.sourceLocation().offset);
923 break;
924 }
925 case BindingTargetSpecifier::UnnamedPropertyTarget: {
926 // Implicit default property bindings are appended in file order,
927 // matching QmlIR::Object::appendBinding().
928 m_propertyBindingsArray.emplaceBack(
929 binding.propertyName(), binding.sourceLocation().offset);
930 break;
931 }
932 default: {
933 Q_UNREACHABLE();
934 break;
935 }
936 }
937}
938
939QList<QQmlJSMetaPropertyBinding> QQmlJSScope::ownPropertyBindingsInQmlIROrder() const
940{
941 QList<QQmlJSMetaPropertyBinding> qmlIrOrdered;
942 qmlIrOrdered.reserve(m_propertyBindingsArray.size());
943
944 for (const auto &data : m_propertyBindingsArray) {
945 const auto [first, last] = m_propertyBindings.equal_range(data.propertyName);
946 Q_ASSERT(first != last);
947 auto binding = std::find_if(first, last, [&](const QQmlJSMetaPropertyBinding &x) {
948 return x.sourceLocation().offset == data.sourceLocationOffset;
949 });
950 Q_ASSERT(binding != last);
951 qmlIrOrdered.append(*binding);
952 }
953
954 return qmlIrOrdered;
955}
956
957bool QQmlJSScope::hasPropertyBindings(const QString &name) const
958{
959 return QQmlJSUtils::searchBaseAndExtensionTypes(
960 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
961 if (mode != QQmlJSScope::NotExtension) {
962 Q_ASSERT(!scope->hasOwnPropertyBindings(name));
963 return false;
964 }
965 return scope->hasOwnPropertyBindings(name);
966 });
967}
968
969QList<QQmlJSMetaPropertyBinding> QQmlJSScope::propertyBindings(const QString &name) const
970{
971 QList<QQmlJSMetaPropertyBinding> bindings;
972 QQmlJSUtils::searchBaseAndExtensionTypes(
973 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
974 if (mode != QQmlJSScope::NotExtension) {
975 Q_ASSERT(!scope->hasOwnPropertyBindings(name));
976 return false;
977 }
978 const auto range = scope->ownPropertyBindings(name);
979 for (auto it = range.first; it != range.second; ++it)
980 bindings.append(*it);
981 return false;
982 });
983 return bindings;
984}
985
986bool QQmlJSScope::hasInterface(const QString &name) const
987{
988 return QQmlJSUtils::searchBaseAndExtensionTypes(
989 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
990 if (mode != QQmlJSScope::NotExtension)
991 return false;
992 return scope->m_interfaceNames.contains(name);
993 });
994}
995
996bool QQmlJSScope::isNameDeferred(const QString &name) const
997{
998 bool isDeferred = false;
999
1000 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
1001 const QStringList immediate = scope->ownImmediateNames();
1002 if (!immediate.isEmpty()) {
1003 isDeferred = !immediate.contains(name);
1004 return true;
1005 }
1006 const QStringList deferred = scope->ownDeferredNames();
1007 if (!deferred.isEmpty()) {
1008 isDeferred = deferred.contains(name);
1009 return true;
1010 }
1011 return false;
1012 });
1013
1014 return isDeferred;
1015}
1016
1017void QQmlJSScope::setBaseTypeName(const QString &baseTypeName)
1018{
1019 m_flags.setFlag(HasBaseTypeError, false);
1020 m_baseTypeNameOrError = baseTypeName;
1021}
1022
1023QString QQmlJSScope::baseTypeName() const
1024{
1025 return m_flags.testFlag(HasBaseTypeError) ? QString() : m_baseTypeNameOrError;
1026}
1027
1028void QQmlJSScope::setBaseTypeError(const QString &baseTypeError)
1029{
1030 m_flags.setFlag(HasBaseTypeError);
1031 m_baseTypeNameOrError = baseTypeError;
1032}
1033
1034/*!
1035\internal
1036The name of the module is only saved in the QmlComponent. Iterate through the parent scopes until
1037the QmlComponent or the root is reached to find out the module name of the component in which `this`
1038resides.
1039*/
1040QString QQmlJSScope::moduleName() const
1041{
1042 for (const QQmlJSScope *it = this; it; it = it->parentScope().get()) {
1043 const QString name = it->ownModuleName();
1044 if (!name.isEmpty())
1045 return name;
1046 }
1047 return {};
1048}
1049
1050QString QQmlJSScope::baseTypeError() const
1051{
1052 return m_flags.testFlag(HasBaseTypeError) ? m_baseTypeNameOrError : QString();
1053}
1054
1055QString QQmlJSScope::attachedTypeName() const
1056{
1057 QString name;
1058 QQmlJSUtils::searchBaseAndExtensionTypes(
1059 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
1060 if (mode != QQmlJSScope::NotExtension)
1061 return false;
1062 if (scope->ownAttachedType().isNull())
1063 return false;
1064 name = scope->ownAttachedTypeName();
1065 return true;
1066 });
1067
1068 return name;
1069}
1070
1071QQmlJSScope::ConstPtr QQmlJSScope::attachedType() const
1072{
1073 QQmlJSScope::ConstPtr ptr;
1074 QQmlJSUtils::searchBaseAndExtensionTypes(
1075 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
1076 if (mode != QQmlJSScope::NotExtension)
1077 return false;
1078 if (scope->ownAttachedType().isNull())
1079 return false;
1080 ptr = scope->ownAttachedType();
1081 return true;
1082 });
1083
1084 return ptr;
1085}
1086
1087QQmlJSScope::AnnotatedScope QQmlJSScope::extensionType() const
1088{
1089 if (!m_extensionType)
1090 return { m_extensionType, NotExtension };
1091 if (m_flags & ExtensionIsJavaScript)
1092 return { m_extensionType, ExtensionJavaScript };
1093 if (m_flags & ExtensionIsNamespace)
1094 return { m_extensionType, ExtensionNamespace };
1095 return { m_extensionType, ExtensionType };
1096}
1097
1098void QQmlJSScope::addOwnRuntimeFunctionIndex(QQmlJSMetaMethod::AbsoluteFunctionIndex index)
1099{
1100 m_runtimeFunctionIndices.emplaceBack(index);
1101}
1102
1103bool QQmlJSScope::isResolved() const
1104{
1105 const bool nameIsEmpty = (m_scopeType == ScopeType::AttachedPropertyScope
1106 || m_scopeType == ScopeType::GroupedPropertyScope)
1107 ? m_internalName.isEmpty()
1108 : m_baseTypeNameOrError.isEmpty();
1109 if (nameIsEmpty)
1110 return true;
1111 if (m_baseType.scope.isNull())
1112 return false;
1113 if (isComposite() && !nonCompositeBaseType(baseType()))
1114 return false;
1115 return true;
1116}
1117
1118QString QQmlJSScope::defaultPropertyName() const
1119{
1120 QString name;
1121 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
1122 name = scope->ownDefaultPropertyName();
1123 return !name.isEmpty();
1124 });
1125 return name;
1126}
1127
1128QString QQmlJSScope::parentPropertyName() const
1129{
1130 QString name;
1131 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
1132 name = scope->ownParentPropertyName();
1133 return !name.isEmpty();
1134 });
1135 return name;
1136}
1137
1138bool QQmlJSScope::isFullyResolved() const
1139{
1140 bool baseResolved = true;
1141 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
1142 if (!scope->isResolved()) {
1143 baseResolved = false;
1144 return true;
1145 }
1146 return false;
1147 });
1148
1149 return baseResolved;
1150}
1151
1152QQmlJSScope::Export::Export(
1153 QString package, QString type, QTypeRevision version, QTypeRevision revision)
1154 : m_package(std::move(package))
1155 , m_type(std::move(type))
1156 , m_version(std::move(version))
1157 , m_revision(std::move(revision))
1158{
1159}
1160
1161bool QQmlJSScope::Export::isValid() const
1162{
1163 return m_version.isValid() || !m_package.isEmpty() || !m_type.isEmpty();
1164}
1165
1166QDeferredFactory<QQmlJSScope>::QDeferredFactory(QQmlJSImporter *importer, const QString &filePath,
1167 const TypeReader &typeReader)
1168 : m_filePath(filePath),
1169 m_importer(importer),
1170 m_typeReader(typeReader ? typeReader
1171 : [](QQmlJSImporter *importer, const QString &filePath,
1172 const QSharedPointer<QQmlJSScope> &scopeToPopulate) {
1173 QQmlJSTypeReader defaultTypeReader(importer, filePath);
1174 defaultTypeReader(scopeToPopulate);
1175 return defaultTypeReader.errors();
1176 })
1177{
1178}
1179
1180void QDeferredFactory<QQmlJSScope>::populate(const QSharedPointer<QQmlJSScope> &scope) const
1181{
1182 scope->setOwnModuleName(m_moduleName);
1183 scope->setFilePath(m_filePath);
1184
1185 QList<QQmlJS::DiagnosticMessage> errors = m_typeReader(m_importer, m_filePath, scope);
1186 m_importer->m_globalWarnings.append(errors);
1187
1188 scope->setInternalName(internalName());
1189 QQmlJSScope::resolveEnums(
1190 scope, m_importer->builtinInternalNames().contextualTypes());
1191 QQmlJSScope::resolveList(
1192 scope, m_importer->builtinInternalNames().contextualTypes().arrayType());
1193
1194 if (m_isSingleton && !scope->isSingleton()) {
1195 m_importer->m_globalWarnings.append(
1196 { QStringLiteral(
1197 "Type %1 declared as singleton in qmldir but missing pragma Singleton")
1198 .arg(scope->internalName()),
1199 QtCriticalMsg, QQmlJS::SourceLocation() });
1200 scope->setIsSingleton(true);
1201 } else if (!m_isSingleton && scope->isSingleton()) {
1202 m_importer->m_globalWarnings.append(
1203 { QStringLiteral("Type %1 not declared as singleton in qmldir "
1204 "but using pragma Singleton")
1205 .arg(scope->internalName()),
1206 QtCriticalMsg, QQmlJS::SourceLocation() });
1207 scope->setIsSingleton(false);
1208 }
1209}
1210
1211/*!
1212 \internal
1213 Checks whether \a derived type can be assigned to this type. Returns \c
1214 true if the type hierarchy of \a derived contains a type equal to this.
1215
1216 \note Assigning \a derived to "QVariant" or "QJSValue" is always possible and
1217 the function returns \c true in this case. In addition any "QObject" based \a derived type
1218 can be assigned to a this type if that type is derived from "QQmlComponent".
1219 */
1220bool QQmlJSScope::canAssign(const QQmlJSScope::ConstPtr &derived) const
1221{
1222 if (!derived)
1223 return false;
1224
1225 // expect this and derived types to have non-composite bases
1226 Q_ASSERT(!isComposite() || nonCompositeBaseType(baseType()));
1227 Q_ASSERT(nonCompositeBaseType(derived));
1228
1229 // the logic with isBaseComponent (as well as the way we set this flag)
1230 // feels wrong - QTBUG-101940
1231 const bool isBaseComponent = [this]() {
1232 if (internalName() == u"QQmlComponent")
1233 return true;
1234 else if (isComposite())
1235 return false;
1236 for (auto cppBase = nonCompositeBaseType(baseType()); cppBase;
1237 cppBase = cppBase->baseType()) {
1238 if (cppBase->internalName() == u"QQmlAbstractDelegateComponent")
1239 return true;
1240 }
1241 return false;
1242 }();
1243
1244 QDuplicateTracker<QQmlJSScope::ConstPtr> seen;
1245 for (auto scope = derived; !scope.isNull() && !seen.hasSeen(scope);
1246 scope = scope->baseType()) {
1247 if (isSameType(scope))
1248 return true;
1249 if (isBaseComponent && scope->internalName() == u"QObject"_s)
1250 return true;
1251 }
1252
1253 if (internalName() == u"QVariant"_s || internalName() == u"QJSValue"_s)
1254 return true;
1255
1256 return isListProperty() && elementType()->canAssign(derived);
1257}
1258
1259/*!
1260 \internal
1261 Checks whether this type or its parents have a custom parser.
1262*/
1263bool QQmlJSScope::isInCustomParserParent() const
1264{
1265 for (const auto *scope = this; scope; scope = scope->parentScope().get()) {
1266 if (!scope->baseType().isNull() && scope->baseType()->hasCustomParser())
1267 return true;
1268 }
1269
1270 return false;
1271}
1272
1273/*!
1274 * \internal
1275 * if this->isInlineComponent(), then this getter returns the name of the inline
1276 * component.
1277 */
1278std::optional<QString> QQmlJSScope::inlineComponentName() const
1279{
1280 Q_ASSERT(isInlineComponent() == m_inlineComponentName.has_value());
1281 return m_inlineComponentName;
1282}
1283
1284/*!
1285 * \internal
1286 * If this type is part of an inline component, return its name. Otherwise, if this type
1287 * is part of the document root, return the document root name.
1288 */
1289QQmlJSScope::InlineComponentOrDocumentRootName QQmlJSScope::enclosingInlineComponentName() const
1290{
1291 for (auto *type = this; type; type = type->parentScope().get()) {
1292 if (type->isInlineComponent())
1293 return *type->inlineComponentName();
1294 }
1295 return RootDocumentNameType();
1296}
1297
1298QList<QQmlJSScope::ConstPtr> QQmlJSScope::childScopes() const
1299{
1300 QList<QQmlJSScope::ConstPtr> result;
1301 result.reserve(m_childScopes.size());
1302 for (const auto &child : m_childScopes)
1303 result.append(child);
1304 return result;
1305}
1306
1307/*!
1308 \internal
1309
1310 Returns true if this type or any base type of it has the "EnforcesScopedEnums" flag.
1311 The rationale is that you can turn on enforcement of scoped enums, but you cannot turn
1312 it off explicitly.
1313 */
1314bool QQmlJSScope::enforcesScopedEnums() const
1315{
1316 for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
1317 if (scope->hasEnforcesScopedEnumsFlag())
1318 return true;
1319 }
1320 return false;
1321}
1322
1323/*!
1324 \internal
1325 Returns true if the current type is creatable by checking all the required base classes.
1326 "Uncreatability" is only inherited from base types for composite types (in qml) and not for non-composite types (c++).
1327
1328For the exact definition:
1329A type is uncreatable if and only if one of its composite base type or its first non-composite base type matches
1330 following criteria:
1331 \list
1332 \li the base type is a singleton, or
1333 \li the base type is an attached type, or
1334 \li the base type is a C++ type with the QML_UNCREATABLE or QML_ANONYMOUS macro, or
1335 \li the base type is a type without default constructor (in that case, it really needs QML_UNCREATABLE or QML_ANONYMOUS)
1336 \endlist
1337 */
1338bool QQmlJSScope::isCreatable() const
1339{
1340 auto isCreatableNonRecursive = [](const QQmlJSScope *scope) {
1341 return scope->hasCreatableFlag() && !scope->isSingleton()
1342 && scope->scopeType() == QQmlSA::ScopeType::QMLScope;
1343 };
1344
1345 for (const QQmlJSScope* scope = this; scope; scope = scope->baseType().get()) {
1346 if (!scope->isComposite()) {
1347 // just check the first nonComposite (c++) base for isCreatableNonRecursive() and then stop
1348 return isCreatableNonRecursive(scope);
1349 } else {
1350 // check all composite (qml) bases for isCreatableNonRecursive().
1351 if (isCreatableNonRecursive(scope))
1352 return true;
1353 }
1354 }
1355 // no uncreatable bases found
1356 return false;
1357}
1358
1359bool QQmlJSScope::isStructured() const
1360{
1361 for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
1362 if (!scope->isComposite())
1363 return scope->hasStructuredFlag();
1364 }
1365 return false;
1366}
1367
1368QQmlSA::Element QQmlJSScope::createQQmlSAElement(const ConstPtr &ptr)
1369{
1370 QQmlSA::Element element;
1371 *reinterpret_cast<QQmlJSScope::ConstPtr *>(element.m_data) = ptr;
1372 return element;
1373}
1374
1375QQmlSA::Element QQmlJSScope::createQQmlSAElement(ConstPtr &&ptr)
1376{
1377 QQmlSA::Element element;
1378 *reinterpret_cast<QQmlJSScope::ConstPtr *>(element.m_data) = std::move(ptr);
1379 return element;
1380}
1381
1382const QQmlJSScope::ConstPtr &QQmlJSScope::scope(const QQmlSA::Element &element)
1383{
1384 return *reinterpret_cast<const QQmlJSScope::ConstPtr *>(element.m_data);
1385}
1386
1387QTypeRevision
1388QQmlJSScope::nonCompositeBaseRevision(const ImportedScope<QQmlJSScope::ConstPtr> &scope)
1389{
1390 for (auto base = scope; base.scope;
1391 base = { base.scope->m_baseType.scope, base.scope->m_baseType.revision }) {
1392 if (!base.scope->isComposite())
1393 return base.revision;
1394 }
1395 return {};
1396}
1397
1398/*!
1399 \internal
1400 Checks whether \a otherScope is the same type as this.
1401
1402 In addition to checking whether the scopes are identical, we also cover duplicate scopes with
1403 the same internal name.
1404 */
1405bool QQmlJSScope::isSameType(const ConstPtr &otherScope) const
1406{
1407 return this == otherScope.get()
1408 || (!this->internalName().isEmpty()
1409 && this->internalName() == otherScope->internalName());
1410}
1411
1412bool QQmlJSScope::inherits(const ConstPtr &base) const
1413{
1414 for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
1415 if (scope->isSameType(base))
1416 return true;
1417 }
1418 return false;
1419}
1420
1421
1422QT_END_NAMESPACE
static QQmlJSScope::ImportedScope< QQmlJSScope::ConstPtr > qFindInlineComponents(QStringView typeName, const QQmlJS::ContextualTypes &contextualTypes)
QQmlJSScope::AnnotatedScope searchOwner(const QQmlJSScope::ConstPtr &self, Predicate &&p)
static QString flagStorage(const QString &underlyingType)
static QTypeRevision resolveTypesInternal(Resolver resolve, ChildScopeUpdater update, const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes, QSet< QString > *usedTypes)