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