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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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