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

source code of qtdeclarative/src/qmlcompiler/qqmljsscope.cpp