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