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

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