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 const QString currentSelector = contextualTypes.currentFileSelector();
407
408 if (type != contextualTypes.types().constEnd()) {
409 // fast path: no selector context active
410 if (currentSelector.isEmpty()) {
411 useType();
412 return *type;
413 }
414 // selector active: discard a mismatched promoted file-selected variant
415 const QString typeSelector = QQmlJSUtils::fileSelectorFor(type->scope);
416 if (typeSelector.isEmpty() || typeSelector == currentSelector) {
417 useType();
418 return *type;
419 }
420 if (auto matching = contextualTypes.fileSelectedTypeFor(name, currentSelector)) {
421 useType();
422 return *matching;
423 }
424 } else if (!currentSelector.isEmpty()) {
425 // try a file-selected variant matching the current selector
426 if (auto matching = contextualTypes.fileSelectedTypeFor(name, currentSelector)) {
427 useType();
428 return *matching;
429 }
430 }
431
432 const auto findListType = [&](const QString &prefix, const QString &postfix)
433 -> ImportedScope<ConstPtr> {
434 if (name.startsWith(prefix) && name.endsWith(postfix)) {
435 const qsizetype prefixLength = prefix.length();
436 const QString &elementName
437 = name.mid(prefixLength, name.length() - prefixLength - postfix.length());
438 const ImportedScope<ConstPtr> element
439 = findType(elementName, contextualTypes, usedTypes);
440 if (element.scope) {
441 useType();
442 return { element.scope->listType(), element.revision };
443 }
444 }
445
446 return {};
447 };
448
449 switch (contextualTypes.context()) {
450 case QQmlJS::ContextualTypes::INTERNAL: {
451 if (const auto listType = findListType(u"QList<"_s, u">"_s);
452 listType.scope && !listType.scope->isReferenceType()) {
453 return listType;
454 }
455
456 if (const auto listType = findListType(u"QQmlListProperty<"_s, u">"_s);
457 listType.scope && listType.scope->isReferenceType()) {
458 return listType;
459 }
460
461 // look for c++ namescoped enums!
462 const auto colonColon = name.lastIndexOf(QStringLiteral("::"));
463 if (colonColon == -1)
464 break;
465
466 const QString outerTypeName = name.left(colonColon);
467 const auto outerType = contextualTypes.types().constFind(outerTypeName);
468 if (outerType == contextualTypes.types().constEnd())
469 break;
470
471 for (const auto &innerType : std::as_const(outerType->scope->m_childScopes)) {
472 if (innerType->m_internalName == name) {
473 useType();
474 return { innerType, outerType->revision };
475 }
476 }
477
478 break;
479 }
480 case QQmlJS::ContextualTypes::QML: {
481 // look after inline components
482 const auto inlineComponent = qFindInlineComponents(name, contextualTypes);
483 if (inlineComponent.scope) {
484 useType();
485 return inlineComponent;
486 }
487
488 if (const auto listType = findListType(u"list<"_s, u">"_s); listType.scope)
489 return listType;
490
491 break;
492 }
493 }
494 return {};
495}
496
497QTypeRevision QQmlJSScope::resolveType(
498 const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &context,
499 QSet<QString> *usedTypes)
500{
501 if (self->accessSemantics() == AccessSemantics::Sequence
502 && self->internalName().startsWith(u"QQmlListProperty<"_s)) {
503 self->setIsListProperty(true);
504 }
505
506 const QString baseTypeName = self->baseTypeName();
507 const auto baseType = findType(baseTypeName, context, usedTypes);
508 if (!self->m_baseType.scope && !baseTypeName.isEmpty())
509 self->m_baseType = { baseType.scope, baseType.revision };
510
511 if (!self->m_attachedType && !self->m_attachedTypeName.isEmpty())
512 self->m_attachedType = findType(self->m_attachedTypeName, context, usedTypes).scope;
513
514 if (!self->m_elementType && !self->m_elementTypeName.isEmpty())
515 self->m_elementType = findType(self->m_elementTypeName, context, usedTypes).scope;
516
517 if (!self->m_extensionType) {
518 if (self->m_extensionTypeName.isEmpty()) {
519 if (self->accessSemantics() == AccessSemantics::Sequence) {
520 // All sequence types are implicitly extended by JS Array.
521 self->setExtensionTypeName(u"Array"_s);
522 self->setExtensionIsJavaScript(true);
523 self->m_extensionType = context.arrayType();
524 }
525 } else {
526 self->m_extensionType = findType(self->m_extensionTypeName, context, usedTypes).scope;
527 }
528 }
529
530
531 for (auto it = self->m_properties.begin(), end = self->m_properties.end(); it != end; ++it) {
532 const QString typeName = it->typeName();
533 if (it->type() || typeName.isEmpty())
534 continue;
535
536 if (const auto type = findType(typeName, context, usedTypes); type.scope) {
537 it->setType(it->isList() ? type.scope->listType() : type.scope);
538 continue;
539 }
540
541 const auto enumeration = self->m_enumerations.find(typeName);
542 if (enumeration != self->m_enumerations.end()) {
543 it->setType(it->isList()
544 ? enumeration->type()->listType()
545 : QQmlJSScope::ConstPtr(enumeration->type()));
546 }
547 }
548
549 const auto resolveParameter = [&](QQmlJSMetaParameter &parameter) {
550 if (const QString typeName = parameter.typeName();
551 !parameter.type() && !typeName.isEmpty()) {
552 auto type = findType(typeName, context, usedTypes);
553 if (type.scope && parameter.isList()) {
554 type.scope = type.scope->listType();
555 parameter.setIsList(false);
556 parameter.setIsPointer(false);
557 parameter.setTypeName(type.scope ? type.scope->internalName() : QString());
558 } else if (type.scope && type.scope->isReferenceType()) {
559 parameter.setIsPointer(true);
560 }
561 parameter.setType({ type.scope });
562 }
563 };
564
565 for (auto it = self->m_methods.begin(), end = self->m_methods.end(); it != end; ++it) {
566 auto returnValue = it->returnValue();
567 resolveParameter(returnValue);
568 it->setReturnValue(returnValue);
569
570 auto parameters = it->parameters();
571 for (int i = 0, length = parameters.size(); i < length; ++i)
572 resolveParameter(parameters[i]);
573 it->setParameters(parameters);
574 }
575
576 for (auto it = self->m_jsIdentifiers.begin(); it != self->m_jsIdentifiers.end(); ++it) {
577 if (it->typeName)
578 it->scope = findType(it->typeName.value(), context, usedTypes).scope;
579 }
580
581 return baseType.revision;
582}
583
584void QQmlJSScope::updateChildScope(
585 const QQmlJSScope::Ptr &childScope, const QQmlJSScope::Ptr &self,
586 const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
587{
588 switch (childScope->scopeType()) {
589 case QQmlSA::ScopeType::GroupedPropertyScope:
590 QQmlJSUtils::searchBaseAndExtensionTypes(
591 self.data(), [&](const QQmlJSScope *type, QQmlJSScope::ExtensionKind mode) {
592 if (mode == QQmlJSScope::ExtensionNamespace)
593 return false;
594 const auto propertyIt = type->m_properties.find(childScope->internalName());
595 if (propertyIt != type->m_properties.end()) {
596 childScope->m_baseType.scope = QQmlJSScope::ConstPtr(propertyIt->type());
597 if (propertyIt->type())
598 childScope->m_semantics = propertyIt->type()->accessSemantics();
599 childScope->setBaseTypeName(propertyIt->typeName());
600 return true;
601 }
602 return false;
603 });
604 break;
605 case QQmlSA::ScopeType::AttachedPropertyScope:
606 if (const auto attachedBase = findType(
607 childScope->internalName(), contextualTypes, usedTypes).scope) {
608 childScope->m_baseType.scope = attachedBase->attachedType();
609 childScope->setBaseTypeName(attachedBase->attachedTypeName());
610 }
611 break;
612 default:
613 break;
614 }
615}
616
617template<typename Resolver, typename ChildScopeUpdater>
619 Resolver resolve, ChildScopeUpdater update, const QQmlJSScope::Ptr &self,
620 const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
621{
622 const QTypeRevision revision = resolve(self, contextualTypes, usedTypes);
623 // NB: constness ensures no detach
624 const auto childScopes = self->childScopes();
625 for (auto it = childScopes.begin(), end = childScopes.end(); it != end; ++it) {
626 const auto childScope = *it;
627 update(childScope, self, contextualTypes, usedTypes);
628 resolveTypesInternal(resolve, update, childScope, contextualTypes, usedTypes); // recursion
629 }
630 return revision;
631}
632
633QTypeRevision QQmlJSScope::resolveTypes(
634 const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
635 QSet<QString> *usedTypes)
636{
637 const auto resolveAll = [](const QQmlJSScope::Ptr &self,
638 const QQmlJS::ContextualTypes &contextualTypes,
639 QSet<QString> *usedTypes) {
640 resolveEnums(self, contextualTypes, usedTypes);
641 resolveList(self, contextualTypes.arrayType());
642 return resolveType(self, contextualTypes, usedTypes);
643 };
644 return resolveTypesInternal(resolveAll, updateChildScope, self, contextualTypes, usedTypes);
645}
646
647void QQmlJSScope::resolveNonEnumTypes(
648 const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
649 QSet<QString> *usedTypes)
650{
651 resolveTypesInternal(resolveType, updateChildScope, self, contextualTypes, usedTypes);
652}
653
654static QString flagStorage(const QString &underlyingType)
655{
656 // All numeric types are builtins. Therefore we can exhaustively check the internal names.
657
658 if (underlyingType == u"uint"
659 || underlyingType == u"quint8"
660 || underlyingType == u"ushort"
661 || underlyingType == u"ulonglong") {
662 return u"uint"_s;
663 }
664
665 if (underlyingType == u"int"
666 || underlyingType == u"qint8"
667 || underlyingType == u"short"
668 || underlyingType == u"longlong") {
669 return u"int"_s;
670 }
671
672 // Will fail to resolve and produce an error on usage.
673 // It's harmless if you never use the enum.
674 return QString();
675}
676
677/*!
678 \internal
679 Resolves all enums of self.
680
681 Some enums happen to have an alias, e.g. when an enum is used as a flag, the enum will exist in
682 two versions, once as enum (e.g. Qt::MouseButton) and once as a flag (e.g. Qt::MouseButtons). In
683 this case, normally only the flag is exposed to the qt metatype system and tools like qmltc will
684 have troubles when encountering the enum in signal parameters etc. To solve this problem,
685 resolveEnums() will create a QQmlJSMetaEnum copy for the alias in case the 'self'-scope already
686 does not have an enum called like the alias.
687 */
688void QQmlJSScope::resolveEnums(
689 const QQmlJSScope::Ptr &self, const QQmlJS::ContextualTypes &contextualTypes,
690 QSet<QString> *usedTypes)
691{
692 // temporary hash to avoid messing up m_enumerations while iterators are active on it
693 QHash<QString, QQmlJSMetaEnum> toBeAppended;
694 for (auto it = self->m_enumerations.begin(), end = self->m_enumerations.end(); it != end; ++it) {
695 if (it->type())
696 continue;
697 QQmlJSScope::Ptr enumScope = QQmlJSScope::create();
698 reparent(self, enumScope);
699 enumScope->m_scopeType = QQmlSA::ScopeType::EnumScope;
700
701 QString typeName = it->typeName();
702 if (typeName.isEmpty())
703 typeName = QStringLiteral("int");
704 else if (it->isFlag())
705 typeName = flagStorage(typeName);
706 enumScope->setBaseTypeName(typeName);
707 const auto type = findType(typeName, contextualTypes, usedTypes);
708 enumScope->m_baseType = { type.scope, type.revision };
709
710 enumScope->m_semantics = AccessSemantics::Value;
711 enumScope->m_internalName = self->internalName() + QStringLiteral("::") + it->name();
712 resolveList(enumScope, contextualTypes.arrayType());
713 if (QString alias = it->alias(); !alias.isEmpty()
714 && self->m_enumerations.constFind(alias) == self->m_enumerations.constEnd()) {
715 auto aliasScope = QQmlJSScope::create();
716 *aliasScope = *enumScope;
717 reparent(self, aliasScope);
718 aliasScope->m_internalName = self->internalName() + QStringLiteral("::") + alias;
719 QQmlJSMetaEnum cpy(*it);
720 cpy.setType(QQmlJSScope::ConstPtr(aliasScope));
721 toBeAppended.insert(alias, cpy);
722 }
723 it->setType(QQmlJSScope::ConstPtr(enumScope));
724 }
725 // no more iterators active on m_enumerations, so it can be changed safely now
726 self->m_enumerations.insert(toBeAppended);
727}
728
729void QQmlJSScope::resolveList(const QQmlJSScope::Ptr &self, const QQmlJSScope::ConstPtr &arrayType)
730{
731 if (self->listType() || self->accessSemantics() == AccessSemantics::Sequence)
732 return;
733
734 Q_ASSERT(!arrayType.isNull());
735 QQmlJSScope::Ptr listType = QQmlJSScope::create();
736 listType->setAccessSemantics(AccessSemantics::Sequence);
737 listType->setElementTypeName(self->internalName());
738
739 if (self->isComposite()) {
740 // There is no internalName for this thing. Just set the value type right away
741 listType->setInternalName(u"QQmlListProperty<>"_s);
742 listType->m_elementType = QQmlJSScope::ConstPtr(self);
743 } else if (self->isReferenceType()) {
744 listType->setInternalName(u"QQmlListProperty<%2>"_s.arg(self->internalName()));
745 // Do not set a filePath on the list type, so that we have to generalize it
746 // even in direct mode.
747 } else {
748 listType->setInternalName(u"QList<%2>"_s.arg(self->internalName()));
749 listType->setFilePath(self->filePath());
750 }
751
752 const QQmlJS::ContextualType element = { self, QTypeRevision(),
753 quint8(QQmlJS::PrecedenceValues::Default) };
754 const QQmlJSImportedScope array = {arrayType, QTypeRevision()};
755 QQmlJS::ContextualTypes contextualTypes(
756 QQmlJS::ContextualTypes::INTERNAL,
757 { { self->internalName(), element }, },
758 { { self, self->internalName() }, },
759 arrayType);
760 QQmlJSScope::resolveTypes(listType, contextualTypes);
761
762 Q_ASSERT(listType->elementType() == self);
763 self->m_listType = listType;
764}
765
766void QQmlJSScope::resolveGroup(
767 const Ptr &self, const ConstPtr &baseType,
768 const QQmlJS::ContextualTypes &contextualTypes, QSet<QString> *usedTypes)
769{
770 Q_ASSERT(baseType);
771 // Generalized group properties are always composite,
772 // which means we expect contextualTypes to be QML names.
773 Q_ASSERT(self->isComposite());
774
775 self->m_baseType.scope = baseType;
776 self->m_semantics = baseType->accessSemantics();
777 resolveNonEnumTypes(self, contextualTypes, usedTypes);
778}
779
780QQmlJSScope::ConstPtr QQmlJSScope::findCurrentQMLScope(const QQmlJSScope::ConstPtr &scope)
781{
782 auto qmlScope = scope;
783 while (qmlScope && qmlScope->m_scopeType != QQmlSA::ScopeType::QMLScope)
784 qmlScope = qmlScope->parentScope();
785 return qmlScope;
786}
787
788bool QQmlJSScope::hasProperty(const QString &name) const
789{
790 return QQmlJSUtils::searchBaseAndExtensionTypes(
791 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
792 if (mode == QQmlJSScope::ExtensionNamespace)
793 return false;
794 return scope->m_properties.contains(name);
795 });
796}
797
798QQmlJSMetaProperty QQmlJSScope::property(const QString &name) const
799{
800 QQmlJSMetaProperty prop;
801 QQmlJSUtils::searchBaseAndExtensionTypes(
802 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
803 if (mode == QQmlJSScope::ExtensionNamespace)
804 return false;
805 const auto it = scope->m_properties.find(name);
806 if (it == scope->m_properties.end())
807 return false;
808 prop = *it;
809 return true;
810 });
811 return prop;
812}
813
814/*!
815 Returns all properties visible from this scope including those of
816 base types and extensions.
817
818 \note Properties that get shadowed are not included and only the
819 version visible from this scope is contained.
820*/
821QHash<QString, QQmlJSMetaProperty> QQmlJSScope::properties() const
822{
823 QHash<QString, QQmlJSMetaProperty> results;
824 QQmlJSUtils::searchBaseAndExtensionTypes(
825 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
826 if (mode == QQmlJSScope::ExtensionNamespace)
827 return false;
828 for (auto it = scope->m_properties.constBegin();
829 it != scope->m_properties.constEnd(); it++) {
830 if (!results.contains(it.key()))
831 results.insert(it.key(), it.value());
832 }
833 return false;
834 });
835 return results;
836}
837
838template <typename Predicate>
839QQmlJSScope::AnnotatedScope searchOwner(const QQmlJSScope::ConstPtr &self, Predicate &&p)
840{
841 QQmlJSScope::AnnotatedScope owner;
842 QQmlJSUtils::searchBaseAndExtensionTypes(
843 self, [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
844 if (mode == QQmlJSScope::ExtensionNamespace)
845 return false;
846 if (p(scope)) {
847 owner = { scope, mode };
848 return true;
849 }
850 return false;
851 });
852 return owner;
853}
854
855QQmlJSScope::AnnotatedScope QQmlJSScope::ownerOfProperty(const QQmlJSScope::ConstPtr &self,
856 const QString &name)
857{
858 return searchOwner(self, [&name](const QQmlJSScope::ConstPtr &scope) {
859 return scope->hasOwnProperty(name);
860 });
861}
862
863QQmlJSScope::AnnotatedScope QQmlJSScope::ownerOfMethod(const QQmlJSScope::ConstPtr &self,
864 const QString &name)
865{
866 return searchOwner(self, [&name](const QQmlJSScope::ConstPtr &scope) {
867 return scope->hasOwnMethod(name);
868 });
869}
870
871QQmlJSScope::AnnotatedScope QQmlJSScope::ownerOfEnum(const QQmlJSScope::ConstPtr &self,
872 const QString &name)
873{
874 return searchOwner(self, [&name](const QQmlJSScope::ConstPtr &scope) {
875 return scope->hasOwnEnumeration(name);
876 });
877}
878
879void QQmlJSScope::setPropertyLocallyRequired(const QString &name, bool isRequired)
880{
881 if (!isRequired)
882 m_requiredPropertyNames.removeOne(name);
883 else if (!m_requiredPropertyNames.contains(name))
884 m_requiredPropertyNames.append(name);
885}
886
887bool QQmlJSScope::isPropertyRequired(const QString &name) const
888{
889 bool isRequired = false;
890 QQmlJSUtils::searchBaseAndExtensionTypes(
891 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
892 if (scope->isPropertyLocallyRequired(name)) {
893 isRequired = true;
894 return true;
895 }
896
897 // the hasOwnProperty() below only makes sense if our scope is
898 // not an extension namespace
899 if (mode == QQmlJSScope::ExtensionNamespace)
900 return false;
901
902 // If it has a property of that name, and that is not required, then none of the
903 // base types matter. You cannot make a derived type's property required with
904 // a "required" specification in a base type.
905 return scope->hasOwnProperty(name);
906 });
907 return isRequired;
908}
909
910bool QQmlJSScope::isPropertyLocallyRequired(const QString &name) const
911{
912 return m_requiredPropertyNames.contains(name);
913}
914
915void QQmlJSScope::addOwnPropertyBinding(const QQmlJSMetaPropertyBinding &binding, BindingTargetSpecifier specifier)
916{
917 Q_ASSERT(binding.sourceLocation().isValid());
918 m_propertyBindings.insert(binding.propertyName(), binding);
919
920 // NB: insert() prepends \a binding to the list of bindings, but we need
921 // append, so rotate
922 using iter = typename QMultiHash<QString, QQmlJSMetaPropertyBinding>::iterator;
923 std::pair<iter, iter> r = m_propertyBindings.equal_range(binding.propertyName());
924 std::rotate(r.first, std::next(r.first), r.second);
925
926 // additionally store bindings in the QmlIR compatible order
927 addOwnPropertyBindingInQmlIROrder(binding, specifier);
928 Q_ASSERT(m_propertyBindings.size() == m_propertyBindingsArray.size());
929}
930
931void QQmlJSScope::addOwnPropertyBindingInQmlIROrder(const QQmlJSMetaPropertyBinding &binding,
932 BindingTargetSpecifier specifier)
933{
934 // the order:
935 // * ordinary bindings are prepended to the binding array
936 // * list bindings are properly ordered within each other, so basically
937 // prepended "in bulk"
938 // * bindings to default properties (which are not explicitly mentioned in
939 // binding expression) are inserted by source location's offset
940
941 static_assert(QTypeInfo<QQmlJSScope::QmlIRCompatibilityBindingData>::isRelocatable,
942 "We really want T to be relocatable as it improves QList<T> performance");
943
944 switch (specifier) {
945 case BindingTargetSpecifier::SimplePropertyTarget: {
946 m_propertyBindingsArray.emplaceFront(binding.propertyName(),
947 binding.sourceLocation().offset);
948 break;
949 }
950 case BindingTargetSpecifier::ListPropertyTarget: {
951 const auto bindingOnTheSameProperty =
952 [&](const QQmlJSScope::QmlIRCompatibilityBindingData &x) {
953 return x.propertyName == binding.propertyName();
954 };
955 // fake "prepend in bulk" by appending a list binding to the sequence of
956 // bindings to the same property. there's an implicit QML language
957 // guarantee that such sequence does not contain arbitrary in-between
958 // bindings that do not belong to the same list property
959 auto pos = std::find_if_not(m_propertyBindingsArray.begin(), m_propertyBindingsArray.end(),
960 bindingOnTheSameProperty);
961 Q_ASSERT(pos == m_propertyBindingsArray.begin()
962 || std::prev(pos)->propertyName == binding.propertyName());
963 m_propertyBindingsArray.emplace(pos, binding.propertyName(),
964 binding.sourceLocation().offset);
965 break;
966 }
967 case BindingTargetSpecifier::UnnamedPropertyTarget: {
968 // Implicit default property bindings are appended in file order,
969 // matching QmlIR::Object::appendBinding().
970 m_propertyBindingsArray.emplaceBack(
971 binding.propertyName(), binding.sourceLocation().offset);
972 break;
973 }
974 default: {
975 Q_UNREACHABLE();
976 break;
977 }
978 }
979}
980
981QList<QQmlJSMetaPropertyBinding> QQmlJSScope::ownPropertyBindingsInQmlIROrder() const
982{
983 QList<QQmlJSMetaPropertyBinding> qmlIrOrdered;
984 qmlIrOrdered.reserve(m_propertyBindingsArray.size());
985
986 for (const auto &data : m_propertyBindingsArray) {
987 const auto [first, last] = m_propertyBindings.equal_range(data.propertyName);
988 Q_ASSERT(first != last);
989 auto binding = std::find_if(first, last, [&](const QQmlJSMetaPropertyBinding &x) {
990 return x.sourceLocation().offset == data.sourceLocationOffset;
991 });
992 Q_ASSERT(binding != last);
993 qmlIrOrdered.append(*binding);
994 }
995
996 return qmlIrOrdered;
997}
998
999bool QQmlJSScope::hasPropertyBindings(const QString &name) const
1000{
1001 return QQmlJSUtils::searchBaseAndExtensionTypes(
1002 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
1003 if (mode != QQmlJSScope::NotExtension) {
1004 Q_ASSERT(!scope->hasOwnPropertyBindings(name));
1005 return false;
1006 }
1007 return scope->hasOwnPropertyBindings(name);
1008 });
1009}
1010
1011QList<QQmlJSMetaPropertyBinding> QQmlJSScope::propertyBindings(const QString &name) const
1012{
1013 QList<QQmlJSMetaPropertyBinding> bindings;
1014 QQmlJSUtils::searchBaseAndExtensionTypes(
1015 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
1016 if (mode != QQmlJSScope::NotExtension) {
1017 Q_ASSERT(!scope->hasOwnPropertyBindings(name));
1018 return false;
1019 }
1020 const auto range = scope->ownPropertyBindings(name);
1021 for (auto it = range.first; it != range.second; ++it)
1022 bindings.append(*it);
1023 return false;
1024 });
1025 return bindings;
1026}
1027
1028bool QQmlJSScope::hasInterface(const QString &name) const
1029{
1030 return QQmlJSUtils::searchBaseAndExtensionTypes(
1031 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
1032 if (mode != QQmlJSScope::NotExtension)
1033 return false;
1034 return scope->m_interfaceNames.contains(name);
1035 });
1036}
1037
1038bool QQmlJSScope::isNameDeferred(const QString &name) const
1039{
1040 bool isDeferred = false;
1041
1042 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
1043 const QStringList immediate = scope->ownImmediateNames();
1044 if (!immediate.isEmpty()) {
1045 isDeferred = !immediate.contains(name);
1046 return true;
1047 }
1048 const QStringList deferred = scope->ownDeferredNames();
1049 if (!deferred.isEmpty()) {
1050 isDeferred = deferred.contains(name);
1051 return true;
1052 }
1053 return false;
1054 });
1055
1056 return isDeferred;
1057}
1058
1059void QQmlJSScope::setBaseTypeName(const QString &baseTypeName)
1060{
1061 m_flags.setFlag(HasBaseTypeError, false);
1062 m_baseTypeNameOrError = baseTypeName;
1063}
1064
1065QString QQmlJSScope::baseTypeName() const
1066{
1067 return m_flags.testFlag(HasBaseTypeError) ? QString() : m_baseTypeNameOrError;
1068}
1069
1070void QQmlJSScope::setBaseTypeError(const QString &baseTypeError)
1071{
1072 m_flags.setFlag(HasBaseTypeError);
1073 m_baseTypeNameOrError = baseTypeError;
1074}
1075
1076/*!
1077\internal
1078The name of the module is only saved in the QmlComponent. Iterate through the parent scopes until
1079the QmlComponent or the root is reached to find out the module name of the component in which `this`
1080resides.
1081*/
1082QString QQmlJSScope::moduleName() const
1083{
1084 for (const QQmlJSScope *it = this; it; it = it->parentScope().get()) {
1085 const QString name = it->ownModuleName();
1086 if (!name.isEmpty())
1087 return name;
1088 }
1089 return {};
1090}
1091
1092QString QQmlJSScope::baseTypeError() const
1093{
1094 return m_flags.testFlag(HasBaseTypeError) ? m_baseTypeNameOrError : QString();
1095}
1096
1097QString QQmlJSScope::attachedTypeName() const
1098{
1099 QString name;
1100 QQmlJSUtils::searchBaseAndExtensionTypes(
1101 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
1102 if (mode != QQmlJSScope::NotExtension)
1103 return false;
1104 if (scope->ownAttachedType().isNull())
1105 return false;
1106 name = scope->ownAttachedTypeName();
1107 return true;
1108 });
1109
1110 return name;
1111}
1112
1113QQmlJSScope::ConstPtr QQmlJSScope::attachedType() const
1114{
1115 QQmlJSScope::ConstPtr ptr;
1116 QQmlJSUtils::searchBaseAndExtensionTypes(
1117 this, [&](const QQmlJSScope *scope, QQmlJSScope::ExtensionKind mode) {
1118 if (mode != QQmlJSScope::NotExtension)
1119 return false;
1120 if (scope->ownAttachedType().isNull())
1121 return false;
1122 ptr = scope->ownAttachedType();
1123 return true;
1124 });
1125
1126 return ptr;
1127}
1128
1129QQmlJSScope::AnnotatedScope QQmlJSScope::extensionType() const
1130{
1131 if (!m_extensionType)
1132 return { m_extensionType, NotExtension };
1133 if (m_flags & ExtensionIsJavaScript)
1134 return { m_extensionType, ExtensionJavaScript };
1135 if (m_flags & ExtensionIsNamespace)
1136 return { m_extensionType, ExtensionNamespace };
1137 return { m_extensionType, ExtensionType };
1138}
1139
1140void QQmlJSScope::addOwnRuntimeFunctionIndex(QQmlJSMetaMethod::AbsoluteFunctionIndex index)
1141{
1142 m_runtimeFunctionIndices.emplaceBack(index);
1143}
1144
1145bool QQmlJSScope::isResolved() const
1146{
1147 const bool nameIsEmpty = (m_scopeType == ScopeType::AttachedPropertyScope
1148 || m_scopeType == ScopeType::GroupedPropertyScope)
1149 ? m_internalName.isEmpty()
1150 : m_baseTypeNameOrError.isEmpty();
1151 if (nameIsEmpty)
1152 return true;
1153 if (m_baseType.scope.isNull())
1154 return false;
1155 if (isComposite() && !nonCompositeBaseType(baseType()))
1156 return false;
1157 return true;
1158}
1159
1160QString QQmlJSScope::defaultPropertyName() const
1161{
1162 QString name;
1163 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
1164 name = scope->ownDefaultPropertyName();
1165 return !name.isEmpty();
1166 });
1167 return name;
1168}
1169
1170QString QQmlJSScope::parentPropertyName() const
1171{
1172 QString name;
1173 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
1174 name = scope->ownParentPropertyName();
1175 return !name.isEmpty();
1176 });
1177 return name;
1178}
1179
1180bool QQmlJSScope::isFullyResolved() const
1181{
1182 bool baseResolved = true;
1183 QQmlJSUtils::searchBaseAndExtensionTypes(this, [&](const QQmlJSScope *scope) {
1184 if (!scope->isResolved()) {
1185 baseResolved = false;
1186 return true;
1187 }
1188 return false;
1189 });
1190
1191 return baseResolved;
1192}
1193
1194QQmlJSScope::Export::Export(
1195 QString package, QString type, QTypeRevision version, QTypeRevision revision)
1196 : m_package(std::move(package))
1197 , m_type(std::move(type))
1198 , m_version(std::move(version))
1199 , m_revision(std::move(revision))
1200{
1201}
1202
1203bool QQmlJSScope::Export::isValid() const
1204{
1205 return m_version.isValid() || !m_package.isEmpty() || !m_type.isEmpty();
1206}
1207
1208QDeferredFactory<QQmlJSScope>::QDeferredFactory(QQmlJSImporter *importer,
1209 const QQmlJS::TypeReader &typeReader,
1210 const QString &filePath, const QString &moduleName,
1211 bool isSingleton)
1212 : m_importer(importer),
1213 m_typeReader(typeReader ? typeReader : QQmlJS::TypeReader{ QQmlJS::defaultTypeReader }),
1214 m_filePath(filePath),
1215 m_moduleName(moduleName),
1216 m_isSingleton(isSingleton)
1217{
1218}
1219
1220void QDeferredFactory<QQmlJSScope>::populate(const QSharedPointer<QQmlJSScope> &scope) const
1221{
1222 scope->setOwnModuleName(m_moduleName);
1223 scope->setIsSingleton(m_isSingleton);
1224
1225 m_typeReader(m_importer, m_filePath, scope);
1226}
1227
1228/*!
1229 \internal
1230 Checks whether \a derived type can be assigned to this type. Returns \c
1231 true if the type hierarchy of \a derived contains a type equal to this.
1232
1233 \note Assigning \a derived to "QVariant" or "QJSValue" is always possible and
1234 the function returns \c true in this case. In addition any "QObject" based \a derived type
1235 can be assigned to a this type if that type is derived from "QQmlComponent".
1236 */
1237bool QQmlJSScope::canAssign(const QQmlJSScope::ConstPtr &derived) const
1238{
1239 if (!derived)
1240 return false;
1241
1242 // expect this and derived types to have non-composite bases
1243 Q_ASSERT(!isComposite() || nonCompositeBaseType(baseType()));
1244 Q_ASSERT(nonCompositeBaseType(derived));
1245
1246 // the logic with isBaseComponent (as well as the way we set this flag)
1247 // feels wrong - QTBUG-101940
1248 const bool isBaseComponent = [this]() {
1249 if (internalName() == u"QQmlComponent")
1250 return true;
1251 else if (isComposite())
1252 return false;
1253 for (auto cppBase = nonCompositeBaseType(baseType()); cppBase;
1254 cppBase = cppBase->baseType()) {
1255 if (cppBase->internalName() == u"QQmlAbstractDelegateComponent")
1256 return true;
1257 }
1258 return false;
1259 }();
1260
1261 QDuplicateTracker<QQmlJSScope::ConstPtr> seen;
1262 for (auto scope = derived; !scope.isNull() && !seen.hasSeen(scope);
1263 scope = scope->baseType()) {
1264 if (isSameType(scope))
1265 return true;
1266 if (isBaseComponent && scope->internalName() == u"QObject"_s)
1267 return true;
1268 }
1269
1270 if (internalName() == u"QVariant"_s || internalName() == u"QJSValue"_s)
1271 return true;
1272
1273 return isListProperty() && elementType()->canAssign(derived);
1274}
1275
1276/*!
1277 \internal
1278 Checks whether this type or its parents have a custom parser.
1279*/
1280bool QQmlJSScope::isInCustomParserParent() const
1281{
1282 for (const auto *scope = this; scope; scope = scope->parentScope().get()) {
1283 if (!scope->baseType().isNull() && scope->baseType()->hasCustomParser())
1284 return true;
1285 }
1286
1287 return false;
1288}
1289
1290/*!
1291 * \internal
1292 * if this->isInlineComponent(), then this getter returns the name of the inline
1293 * component.
1294 */
1295std::optional<QString> QQmlJSScope::inlineComponentName() const
1296{
1297 Q_ASSERT(isInlineComponent() == m_inlineComponentName.has_value());
1298 return m_inlineComponentName;
1299}
1300
1301/*!
1302 * \internal
1303 * If this type is part of an inline component, return its name. Otherwise, if this type
1304 * is part of the document root, return the document root name.
1305 */
1306QQmlJSScope::InlineComponentOrDocumentRootName QQmlJSScope::enclosingInlineComponentName() const
1307{
1308 for (auto *type = this; type; type = type->parentScope().get()) {
1309 if (type->isInlineComponent())
1310 return *type->inlineComponentName();
1311 }
1312 return RootDocumentNameType();
1313}
1314
1315QList<QQmlJSScope::ConstPtr> QQmlJSScope::childScopes() const
1316{
1317 QList<QQmlJSScope::ConstPtr> result;
1318 result.reserve(m_childScopes.size());
1319 for (const auto &child : m_childScopes)
1320 result.append(child);
1321 return result;
1322}
1323
1324/*!
1325 \internal
1326
1327 Returns true if this type or any base type of it has the "EnforcesScopedEnums" flag.
1328 The rationale is that you can turn on enforcement of scoped enums, but you cannot turn
1329 it off explicitly.
1330 */
1331bool QQmlJSScope::enforcesScopedEnums() const
1332{
1333 for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
1334 if (scope->hasEnforcesScopedEnumsFlag())
1335 return true;
1336 }
1337 return false;
1338}
1339
1340/*!
1341 \internal
1342 Returns true if the current type is creatable by checking all the required base classes.
1343 "Uncreatability" is only inherited from base types for composite types (in qml) and not for non-composite types (c++).
1344
1345For the exact definition:
1346A type is uncreatable if and only if one of its composite base type or its first non-composite base type matches
1347 following criteria:
1348 \list
1349 \li the base type is a singleton, or
1350 \li the base type is an attached type, or
1351 \li the base type is a C++ type with the QML_UNCREATABLE or QML_ANONYMOUS macro, or
1352 \li the base type is a type without default constructor (in that case, it really needs QML_UNCREATABLE or QML_ANONYMOUS)
1353 \endlist
1354 */
1355bool QQmlJSScope::isCreatable() const
1356{
1357 auto isCreatableNonRecursive = [](const QQmlJSScope *scope) {
1358 return scope->hasCreatableFlag() && !scope->isSingleton()
1359 && scope->scopeType() == QQmlSA::ScopeType::QMLScope;
1360 };
1361
1362 for (const QQmlJSScope* scope = this; scope; scope = scope->baseType().get()) {
1363 if (!scope->isComposite()) {
1364 // just check the first nonComposite (c++) base for isCreatableNonRecursive() and then stop
1365 return isCreatableNonRecursive(scope);
1366 } else {
1367 // check all composite (qml) bases for isCreatableNonRecursive().
1368 if (isCreatableNonRecursive(scope))
1369 return true;
1370 }
1371 }
1372 // no uncreatable bases found
1373 return false;
1374}
1375
1376bool QQmlJSScope::isStructured() const
1377{
1378 for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
1379 if (!scope->isComposite())
1380 return scope->hasStructuredFlag();
1381 }
1382 return false;
1383}
1384
1385QQmlSA::Element QQmlJSScope::createQQmlSAElement(const ConstPtr &ptr)
1386{
1387 QQmlSA::Element element;
1388 *reinterpret_cast<QQmlJSScope::ConstPtr *>(element.m_data) = ptr;
1389 return element;
1390}
1391
1392QQmlSA::Element QQmlJSScope::createQQmlSAElement(ConstPtr &&ptr)
1393{
1394 QQmlSA::Element element;
1395 *reinterpret_cast<QQmlJSScope::ConstPtr *>(element.m_data) = std::move(ptr);
1396 return element;
1397}
1398
1399const QQmlJSScope::ConstPtr &QQmlJSScope::scope(const QQmlSA::Element &element)
1400{
1401 return *reinterpret_cast<const QQmlJSScope::ConstPtr *>(element.m_data);
1402}
1403
1404QTypeRevision
1405QQmlJSScope::nonCompositeBaseRevision(const ImportedScope<QQmlJSScope::ConstPtr> &scope)
1406{
1407 for (auto base = scope; base.scope;
1408 base = { base.scope->m_baseType.scope, base.scope->m_baseType.revision }) {
1409 if (!base.scope->isComposite())
1410 return base.revision;
1411 }
1412 return {};
1413}
1414
1415/*!
1416 \internal
1417 Checks whether \a otherScope is the same type as this.
1418
1419 In addition to checking whether the scopes are identical, we also cover duplicate scopes with
1420 the same internal name.
1421 */
1422bool QQmlJSScope::isSameType(const ConstPtr &otherScope) const
1423{
1424 return this == otherScope.get()
1425 || (!this->internalName().isEmpty()
1426 && this->internalName() == otherScope->internalName());
1427}
1428
1429bool QQmlJSScope::inherits(const ConstPtr &base) const
1430{
1431 for (const QQmlJSScope *scope = this; scope; scope = scope->baseType().get()) {
1432 if (scope->isSameType(base))
1433 return true;
1434 }
1435 return false;
1436}
1437
1438
1439QT_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)