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