1// Copyright (C) 2021 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 "qqmljstyperesolver_p.h"
5
6#include "qqmljsimporter_p.h"
7#include "qqmljsimportvisitor_p.h"
8#include "qqmljslogger_p.h"
9#include "qqmljsutils_p.h"
10#include <private/qv4value_p.h>
11
12#include <private/qduplicatetracker_p.h>
13
14#include <QtCore/qloggingcategory.h>
15
16QT_BEGIN_NAMESPACE
17
18using namespace Qt::StringLiterals;
19
20Q_LOGGING_CATEGORY(lcTypeResolver, "qt.qml.compiler.typeresolver", QtInfoMsg);
21
22static inline void assertExtension(const QQmlJSScope::ConstPtr &type, QLatin1String extension)
23{
24 Q_ASSERT(type);
25 Q_ASSERT(type->extensionType().scope->internalName() == extension);
26 Q_ASSERT(type->extensionIsJavaScript());
27}
28
29QQmlJSTypeResolver::QQmlJSTypeResolver(QQmlJSImporter *importer)
30 : m_imports(importer->builtinInternalNames()),
31 m_trackedTypes(std::make_unique<QHash<QQmlJSScope::ConstPtr, TrackedType>>())
32{
33 const QQmlJSImporter::ImportedTypes &builtinTypes = m_imports;
34
35 m_voidType = builtinTypes.type(name: u"void"_s).scope;
36 assertExtension(type: m_voidType, extension: "undefined"_L1);
37
38 m_nullType = builtinTypes.type(name: u"std::nullptr_t"_s).scope;
39 Q_ASSERT(m_nullType);
40
41 m_realType = builtinTypes.type(name: u"double"_s).scope;
42 assertExtension(type: m_realType, extension: "Number"_L1);
43
44 m_floatType = builtinTypes.type(name: u"float"_s).scope;
45 assertExtension(type: m_floatType, extension: "Number"_L1);
46
47 m_int8Type = builtinTypes.type(name: u"qint8"_s).scope;
48 assertExtension(type: m_int8Type, extension: "Number"_L1);
49
50 m_uint8Type = builtinTypes.type(name: u"quint8"_s).scope;
51 assertExtension(type: m_uint8Type, extension: "Number"_L1);
52
53 m_int16Type = builtinTypes.type(name: u"short"_s).scope;
54 assertExtension(type: m_int16Type, extension: "Number"_L1);
55
56 m_uint16Type = builtinTypes.type(name: u"ushort"_s).scope;
57 assertExtension(type: m_uint16Type, extension: "Number"_L1);
58
59 m_int32Type = builtinTypes.type(name: u"int"_s).scope;
60 assertExtension(type: m_int32Type, extension: "Number"_L1);
61
62 m_uint32Type = builtinTypes.type(name: u"uint"_s).scope;
63 assertExtension(type: m_uint32Type, extension: "Number"_L1);
64
65 m_int64Type = builtinTypes.type(name: u"qlonglong"_s).scope;
66 Q_ASSERT(m_int64Type);
67
68 m_uint64Type = builtinTypes.type(name: u"qulonglong"_s).scope;
69 Q_ASSERT(m_uint64Type);
70
71 m_sizeType = builtinTypes.type(name: u"qsizetype"_s).scope;
72 assertExtension(type: m_sizeType, extension: "Number"_L1);
73
74 // qsizetype is either a 32bit or a 64bit signed integer. We don't want to special-case it.
75 Q_ASSERT(m_sizeType == m_int32Type || m_sizeType == m_int64Type);
76
77 m_boolType = builtinTypes.type(name: u"bool"_s).scope;
78 assertExtension(type: m_boolType, extension: "Boolean"_L1);
79
80 m_stringType = builtinTypes.type(name: u"QString"_s).scope;
81 assertExtension(type: m_stringType, extension: "String"_L1);
82
83 m_stringListType = builtinTypes.type(name: u"QStringList"_s).scope;
84 assertExtension(type: m_stringListType, extension: "Array"_L1);
85
86 m_byteArrayType = builtinTypes.type(name: u"QByteArray"_s).scope;
87 assertExtension(type: m_byteArrayType, extension: "ArrayBuffer"_L1);
88
89 m_urlType = builtinTypes.type(name: u"QUrl"_s).scope;
90 assertExtension(type: m_urlType, extension: "URL"_L1);
91
92 m_dateTimeType = builtinTypes.type(name: u"QDateTime"_s).scope;
93 assertExtension(type: m_dateTimeType, extension: "Date"_L1);
94
95 m_dateType = builtinTypes.type(name: u"QDate"_s).scope;
96 Q_ASSERT(m_dateType);
97
98 m_timeType = builtinTypes.type(name: u"QTime"_s).scope;
99 Q_ASSERT(m_timeType);
100
101 m_variantListType = builtinTypes.type(name: u"QVariantList"_s).scope;
102 assertExtension(type: m_variantListType, extension: "Array"_L1);
103
104 m_variantMapType = builtinTypes.type(name: u"QVariantMap"_s).scope;
105 Q_ASSERT(m_variantMapType);
106 m_varType = builtinTypes.type(name: u"QVariant"_s).scope;
107 Q_ASSERT(m_varType);
108
109 m_jsValueType = builtinTypes.type(name: u"QJSValue"_s).scope;
110 Q_ASSERT(m_jsValueType);
111
112 m_qObjectType = builtinTypes.type(name: u"QObject"_s).scope;
113 assertExtension(type: m_qObjectType, extension: "Object"_L1);
114
115 m_qObjectListType = builtinTypes.type(name: u"QObjectList"_s).scope;
116 assertExtension(type: m_qObjectListType, extension: "Array"_L1);
117
118 m_qQmlScriptStringType = builtinTypes.type(name: u"QQmlScriptString"_s).scope;
119 Q_ASSERT(m_qQmlScriptStringType);
120
121 m_functionType = builtinTypes.type(name: u"function"_s).scope;
122 Q_ASSERT(m_functionType);
123
124 m_numberPrototype = builtinTypes.type(name: u"NumberPrototype"_s).scope;
125 Q_ASSERT(m_numberPrototype);
126
127 m_arrayPrototype = builtinTypes.type(name: u"ArrayPrototype"_s).scope;
128 Q_ASSERT(m_arrayPrototype);
129
130 m_listPropertyType = m_qObjectType->listType();
131 Q_ASSERT(m_listPropertyType->internalName() == u"QQmlListProperty<QObject>"_s);
132 Q_ASSERT(m_listPropertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence);
133 Q_ASSERT(m_listPropertyType->valueTypeName() == u"QObject"_s);
134 assertExtension(type: m_listPropertyType, extension: "Array"_L1);
135
136 QQmlJSScope::Ptr emptyType = QQmlJSScope::create();
137 emptyType->setAccessSemantics(QQmlJSScope::AccessSemantics::None);
138 m_emptyType = emptyType;
139
140 QQmlJSScope::Ptr jsPrimitiveType = QQmlJSScope::create();
141 jsPrimitiveType->setInternalName(u"QJSPrimitiveValue"_s);
142 jsPrimitiveType->setFilePath(u"qjsprimitivevalue.h"_s);
143 jsPrimitiveType->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
144 m_jsPrimitiveType = jsPrimitiveType;
145
146 QQmlJSScope::Ptr metaObjectType = QQmlJSScope::create();
147 metaObjectType->setInternalName(u"const QMetaObject"_s);
148 metaObjectType->setFilePath(u"qmetaobject.h"_s);
149 metaObjectType->setAccessSemantics(QQmlJSScope::AccessSemantics::Reference);
150 m_metaObjectType = metaObjectType;
151
152 m_jsGlobalObject = importer->jsGlobalObject();
153
154 QQmlJSScope::Ptr forInIteratorPtr = QQmlJSScope::create();
155 forInIteratorPtr->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
156 forInIteratorPtr->setFilePath(u"qjslist.h"_s);
157 forInIteratorPtr->setInternalName(u"QJSListForInIterator::Ptr"_s);
158 m_forInIteratorPtr = forInIteratorPtr;
159
160 QQmlJSScope::Ptr forOfIteratorPtr = QQmlJSScope::create();
161 forOfIteratorPtr->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
162 forOfIteratorPtr->setFilePath(u"qjslist.h"_s);
163 forOfIteratorPtr->setInternalName(u"QJSListForOfIterator::Ptr"_s);
164 m_forOfIteratorPtr = forOfIteratorPtr;
165}
166
167/*!
168 \internal
169
170 Initializes the type resolver. As part of that initialization, makes \a
171 visitor traverse the program when given.
172*/
173void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *program)
174{
175 m_logger = visitor->logger();
176
177 m_objectsById.clear();
178 m_objectsByLocation.clear();
179 m_imports.clear();
180 m_signalHandlers.clear();
181
182 if (program)
183 program->accept(visitor);
184
185 m_objectsById = visitor->addressableScopes();
186 m_objectsByLocation = visitor->scopesBylocation();
187 m_signalHandlers = visitor->signalHandlers();
188 m_imports = visitor->imports();
189 m_seenModuleQualifiers = visitor->seenModuleQualifiers();
190}
191
192QQmlJSScope::ConstPtr QQmlJSTypeResolver::mathObject() const
193{
194 return jsGlobalObject()->property(name: u"Math"_s).type();
195}
196
197QQmlJSScope::ConstPtr QQmlJSTypeResolver::consoleObject() const
198{
199 return jsGlobalObject()->property(name: u"console"_s).type();
200}
201
202QQmlJSScope::ConstPtr
203QQmlJSTypeResolver::scopeForLocation(const QV4::CompiledData::Location &location) const
204{
205 // #if required for standalone DOM compilation against Qt 6.2
206 qCDebug(lcTypeResolver()).nospace()
207 << "looking for object at " << location.line() << ':' << location.column();
208 return m_objectsByLocation[location];
209}
210
211QQmlJSScope::ConstPtr QQmlJSTypeResolver::typeFromAST(QQmlJS::AST::Type *type) const
212{
213 const QString typeId = QmlIR::IRBuilder::asString(node: type->typeId);
214 if (!type->typeArgument)
215 return m_imports.type(name: typeId).scope;
216 if (typeId == u"list"_s) {
217 if (const QQmlJSScope::ConstPtr typeArgument = typeForName(name: type->typeArgument->toString()))
218 return typeArgument->listType();
219 }
220 return QQmlJSScope::ConstPtr();
221}
222
223QQmlJSScope::ConstPtr QQmlJSTypeResolver::typeForConst(QV4::ReturnedValue rv) const
224{
225 QV4::Value value = QV4::Value::fromReturnedValue(val: rv);
226 if (value.isUndefined())
227 return voidType();
228
229 if (value.isInt32())
230 return int32Type();
231
232 if (value.isBoolean())
233 return boolType();
234
235 if (value.isDouble())
236 return realType();
237
238 if (value.isNull())
239 return nullType();
240
241 if (value.isEmpty())
242 return emptyType();
243
244 return {};
245}
246
247QQmlJSRegisterContent
248QQmlJSTypeResolver::typeForBinaryOperation(QSOperator::Op oper, const QQmlJSRegisterContent &left,
249 const QQmlJSRegisterContent &right) const
250{
251 Q_ASSERT(left.isValid());
252 Q_ASSERT(right.isValid());
253
254 switch (oper) {
255 case QSOperator::Op::Equal:
256 case QSOperator::Op::NotEqual:
257 case QSOperator::Op::StrictEqual:
258 case QSOperator::Op::StrictNotEqual:
259 case QSOperator::Op::Lt:
260 case QSOperator::Op::Gt:
261 case QSOperator::Op::Ge:
262 case QSOperator::Op::In:
263 case QSOperator::Op::Le:
264 return globalType(type: boolType());
265 case QSOperator::Op::BitAnd:
266 case QSOperator::Op::BitOr:
267 case QSOperator::Op::BitXor:
268 case QSOperator::Op::LShift:
269 case QSOperator::Op::RShift:
270 return builtinType(type: int32Type());
271 case QSOperator::Op::URShift:
272 return builtinType(type: uint32Type());
273 case QSOperator::Op::Add: {
274 const auto leftContents = containedType(container: left);
275 const auto rightContents = containedType(container: right);
276 if (equals(a: leftContents, b: stringType()) || equals(a: rightContents, b: stringType()))
277 return builtinType(type: stringType());
278
279 const QQmlJSScope::ConstPtr result = merge(a: leftContents, b: rightContents);
280 if (equals(a: result, b: boolType()))
281 return builtinType(type: int32Type());
282 if (isNumeric(type: result))
283 return builtinType(type: realType());
284
285 return builtinType(type: jsPrimitiveType());
286 }
287 case QSOperator::Op::Sub:
288 case QSOperator::Op::Mul:
289 case QSOperator::Op::Exp: {
290 const QQmlJSScope::ConstPtr result = merge(a: containedType(container: left), b: containedType(container: right));
291 return builtinType(type: equals(a: result, b: boolType()) ? int32Type() : realType());
292 }
293 case QSOperator::Op::Div:
294 case QSOperator::Op::Mod:
295 return builtinType(type: realType());
296 case QSOperator::Op::As:
297 return right;
298 default:
299 break;
300 }
301
302 return merge(a: left, b: right);
303}
304
305QQmlJSRegisterContent QQmlJSTypeResolver::typeForArithmeticUnaryOperation(
306 UnaryOperator op, const QQmlJSRegisterContent &operand) const
307{
308 switch (op) {
309 case UnaryOperator::Not:
310 return builtinType(type: boolType());
311 case UnaryOperator::Complement:
312 return builtinType(type: int32Type());
313 case UnaryOperator::Plus:
314 if (isIntegral(type: operand))
315 return operand;
316 Q_FALLTHROUGH();
317 default:
318 if (equals(a: containedType(container: operand), b: boolType()))
319 return builtinType(type: int32Type());
320 break;
321 }
322
323 return builtinType(type: realType());
324}
325
326bool QQmlJSTypeResolver::isPrimitive(const QQmlJSRegisterContent &type) const
327{
328 return isPrimitive(type: containedType(container: type));
329}
330
331bool QQmlJSTypeResolver::isNumeric(const QQmlJSRegisterContent &type) const
332{
333 return isNumeric(type: containedType(container: type));
334}
335
336bool QQmlJSTypeResolver::isIntegral(const QQmlJSRegisterContent &type) const
337{
338 return isIntegral(type: containedType(container: type));
339}
340
341bool QQmlJSTypeResolver::isIntegral(const QQmlJSScope::ConstPtr &type) const
342{
343 return isSignedInteger(type) || isUnsignedInteger(type);
344}
345
346bool QQmlJSTypeResolver::isPrimitive(const QQmlJSScope::ConstPtr &type) const
347{
348 return isNumeric(type)
349 || equals(a: type, b: m_boolType) || equals(a: type, b: m_voidType) || equals(a: type, b: m_nullType)
350 || equals(a: type, b: m_stringType) || equals(a: type, b: m_jsPrimitiveType);
351}
352
353bool QQmlJSTypeResolver::isNumeric(const QQmlJSScope::ConstPtr &type) const
354{
355 return QQmlJSUtils::searchBaseAndExtensionTypes(
356 type, check: [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
357 if (mode == QQmlJSScope::ExtensionNamespace)
358 return false;
359 return equals(a: scope, b: m_numberPrototype);
360 });
361}
362
363bool QQmlJSTypeResolver::isSignedInteger(const QQmlJSScope::ConstPtr &type) const
364{
365 return equals(a: type, b: m_int8Type)
366 || equals(a: type, b: m_int16Type)
367 || equals(a: type, b: m_int32Type)
368 || equals(a: type, b: m_int64Type);
369}
370
371bool QQmlJSTypeResolver::isUnsignedInteger(const QQmlJSScope::ConstPtr &type) const
372{
373 return equals(a: type, b: m_uint8Type)
374 || equals(a: type, b: m_uint16Type)
375 || equals(a: type, b: m_uint32Type)
376 || equals(a: type, b: m_uint64Type);
377}
378
379bool QQmlJSTypeResolver::isNativeArrayIndex(const QQmlJSScope::ConstPtr &type) const
380{
381 return (equals(a: type, b: m_uint8Type)
382 || equals(a: type, b: m_int8Type)
383 || equals(a: type, b: m_uint16Type)
384 || equals(a: type, b: m_int16Type)
385 || equals(a: type, b: m_uint32Type)
386 || equals(a: type, b: m_int32Type));
387}
388
389QQmlJSScope::ConstPtr
390QQmlJSTypeResolver::containedType(const QQmlJSRegisterContent &container) const
391{
392 if (container.isType())
393 return container.type();
394 if (container.isProperty())
395 return container.property().type();
396 if (container.isEnumeration())
397 return container.enumeration().type();
398 if (container.isMethod())
399 return container.storedType(); // Methods can only be stored in QJSValue.
400 if (container.isImportNamespace()) {
401 switch (container.variant()) {
402 case QQmlJSRegisterContent::ScopeModulePrefix:
403 return container.storedType(); // We don't store scope module prefixes
404 case QQmlJSRegisterContent::ObjectModulePrefix:
405 return container.scopeType(); // We need to pass the original object through.
406 default:
407 Q_UNREACHABLE();
408 }
409 }
410 if (container.isConversion())
411 return container.conversionResult();
412
413 Q_UNREACHABLE_RETURN({});
414}
415
416QQmlJSScope::ConstPtr QQmlJSTypeResolver::trackedType(const QQmlJSScope::ConstPtr &type) const
417{
418 if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
419 return type;
420
421 // If origin is in fact an already tracked type, track the original of that one instead.
422 const auto it = m_trackedTypes->find(key: type);
423 QQmlJSScope::ConstPtr orig = (it == m_trackedTypes->end()) ? type : it->original;
424
425 QQmlJSScope::Ptr clone = QQmlJSScope::clone(origin: orig);
426 m_trackedTypes->insert(key: clone, value: { .original: std::move(orig), .replacement: QQmlJSScope::ConstPtr(), .clone: clone });
427 return clone;
428}
429
430QQmlJSRegisterContent QQmlJSTypeResolver::transformed(
431 const QQmlJSRegisterContent &origin,
432 QQmlJSScope::ConstPtr (QQmlJSTypeResolver::*op)(const QQmlJSScope::ConstPtr &) const) const
433{
434 if (origin.isType()) {
435 return QQmlJSRegisterContent::create(
436 storedType: (this->*op)(origin.storedType()), type: (this->*op)(origin.type()),
437 resultLookupIndex: origin.resultLookupIndex(), variant: origin.variant(), scope: (this->*op)(origin.scopeType()));
438 }
439
440 if (origin.isProperty()) {
441 QQmlJSMetaProperty prop = origin.property();
442 prop.setType((this->*op)(prop.type()));
443 return QQmlJSRegisterContent::create(
444 storedType: (this->*op)(origin.storedType()), property: prop, baseLookupIndex: origin.baseLookupIndex(),
445 resultLookupIndex: origin.resultLookupIndex(), variant: origin.variant(), scope: (this->*op)(origin.scopeType()));
446 }
447
448 if (origin.isEnumeration()) {
449 QQmlJSMetaEnum enumeration = origin.enumeration();
450 enumeration.setType((this->*op)(enumeration.type()));
451 return QQmlJSRegisterContent::create(
452 storedType: (this->*op)(origin.storedType()), enumeration, enumMember: origin.enumMember(),
453 variant: origin.variant(), scope: (this->*op)(origin.scopeType()));
454 }
455
456 if (origin.isMethod()) {
457 return QQmlJSRegisterContent::create(
458 storedType: (this->*op)(origin.storedType()), methods: origin.method(), variant: origin.variant(),
459 scope: (this->*op)(origin.scopeType()));
460 }
461
462 if (origin.isImportNamespace()) {
463 return QQmlJSRegisterContent::create(
464 storedType: (this->*op)(origin.storedType()), importNamespaceStringId: origin.importNamespace(),
465 variant: origin.variant(), scope: (this->*op)(origin.scopeType()));
466 }
467
468 if (origin.isConversion()) {
469 // When retrieving the originals we want a deep retrieval.
470 // When tracking a new type, we don't want to re-track its originals, though.
471
472 const QList<QQmlJSScope::ConstPtr> origins = origin.conversionOrigins();
473 QList<QQmlJSScope::ConstPtr> transformedOrigins;
474 if (op == &QQmlJSTypeResolver::trackedType) {
475 transformedOrigins = origins;
476 } else {
477 transformedOrigins.reserve(asize: origins.length());
478 for (const QQmlJSScope::ConstPtr &origin: origins)
479 transformedOrigins.append(t: (this->*op)(origin));
480 }
481
482 return QQmlJSRegisterContent::create(
483 storedType: (this->*op)(origin.storedType()),
484 origins: transformedOrigins,
485 conversion: (this->*op)(origin.conversionResult()),
486 conversionScope: (this->*op)(origin.conversionResultScope()),
487 variant: origin.variant(), scope: (this->*op)(origin.scopeType()));
488 }
489
490 Q_UNREACHABLE_RETURN({});
491}
492
493QQmlJSRegisterContent QQmlJSTypeResolver::registerContentForName(
494 const QString &name, const QQmlJSScope::ConstPtr &scopeType,
495 bool hasObjectModulePrefix) const
496{
497 QQmlJSScope::ConstPtr type = typeForName(name);
498 if (!type)
499 return QQmlJSRegisterContent();
500
501 if (type->isSingleton()) {
502 return QQmlJSRegisterContent::create(
503 storedType: storedType(type), type, resultLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex,
504 variant: QQmlJSRegisterContent::Singleton, scope: scopeType);
505 }
506
507 if (type->isScript()) {
508 return QQmlJSRegisterContent::create(
509 storedType: storedType(type), type, resultLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex,
510 variant: QQmlJSRegisterContent::Script, scope: scopeType);
511 }
512
513 if (const auto attached = type->attachedType()) {
514 if (!genericType(type: attached)) {
515 m_logger->log(message: u"Cannot resolve generic base of attached %1"_s.arg(
516 a: attached->internalName()),
517 id: qmlCompiler, srcLocation: attached->sourceLocation());
518 return {};
519 } else if (type->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
520 m_logger->log(message: u"Cannot retrieve attached object for non-reference type %1"_s.arg(
521 a: type->internalName()),
522 id: qmlCompiler, srcLocation: type->sourceLocation());
523 return {};
524 } else {
525 // We don't know yet whether we need the attached or the plain object. In direct
526 // mode, we will figure this out using the scope type and access any enums of the
527 // plain type directly. In indirect mode, we can use enum lookups.
528 return QQmlJSRegisterContent::create(
529 storedType: storedType(type: attached), type: attached, resultLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex,
530 variant: hasObjectModulePrefix
531 ? QQmlJSRegisterContent::ObjectAttached
532 : QQmlJSRegisterContent::ScopeAttached, scope: type);
533 }
534 }
535
536 switch (type->accessSemantics()) {
537 case QQmlJSScope::AccessSemantics::None:
538 case QQmlJSScope::AccessSemantics::Reference:
539 // A plain reference to a non-singleton, non-attached type.
540 // We may still need the plain type reference for enum lookups,
541 // Store it as QMetaObject.
542 // This only works with namespaces and object types.
543 return QQmlJSRegisterContent::create(
544 storedType: metaObjectType(), type: metaObjectType(), resultLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex,
545 variant: QQmlJSRegisterContent::MetaType, scope: type);
546 case QQmlJSScope::AccessSemantics::Sequence:
547 case QQmlJSScope::AccessSemantics::Value:
548 if (canAddressValueTypes()) {
549 return QQmlJSRegisterContent::create(
550 storedType: metaObjectType(), type: metaObjectType(), resultLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex,
551 variant: QQmlJSRegisterContent::MetaType, scope: type);
552 }
553 // Else this is not actually a type reference. You cannot get the metaobject
554 // of a value type in QML and sequences don't even have metaobjects.
555 break;
556 }
557
558 return QQmlJSRegisterContent();
559}
560
561QQmlJSRegisterContent QQmlJSTypeResolver::original(const QQmlJSRegisterContent &type) const
562{
563 return transformed(origin: type, op: &QQmlJSTypeResolver::originalType);
564}
565
566QQmlJSRegisterContent QQmlJSTypeResolver::tracked(const QQmlJSRegisterContent &type) const
567{
568 return transformed(origin: type, op: &QQmlJSTypeResolver::trackedType);
569}
570
571QQmlJSScope::ConstPtr QQmlJSTypeResolver::trackedContainedType(
572 const QQmlJSRegisterContent &container) const
573{
574 const QQmlJSScope::ConstPtr type = containedType(container);
575 return m_trackedTypes->contains(key: type) ? type : QQmlJSScope::ConstPtr();
576}
577
578QQmlJSScope::ConstPtr QQmlJSTypeResolver::originalContainedType(
579 const QQmlJSRegisterContent &container) const
580{
581 return originalType(type: containedType(container));
582}
583
584bool QQmlJSTypeResolver::adjustTrackedType(
585 const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const
586{
587 if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
588 return true;
589
590 const auto it = m_trackedTypes->find(key: tracked);
591 Q_ASSERT(it != m_trackedTypes->end());
592
593 // If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
594 // we better not change the type.
595 if (!canPrimitivelyConvertFromTo(from: tracked, to: conversion)
596 && !canPopulate(type: conversion, argument: tracked, isExtension: nullptr)
597 && !selectConstructor(type: conversion, argument: tracked, isExtension: nullptr).isValid()) {
598 return false;
599 }
600
601 it->replacement = comparableType(type: conversion);
602 *it->clone = std::move(*QQmlJSScope::clone(origin: conversion));
603 return true;
604}
605
606bool QQmlJSTypeResolver::adjustTrackedType(
607 const QQmlJSScope::ConstPtr &tracked, const QList<QQmlJSScope::ConstPtr> &conversions) const
608{
609 if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
610 return true;
611
612 const auto it = m_trackedTypes->find(key: tracked);
613 Q_ASSERT(it != m_trackedTypes->end());
614 QQmlJSScope::Ptr mutableTracked = it->clone;
615 QQmlJSScope::ConstPtr result;
616 for (const QQmlJSScope::ConstPtr &type : conversions)
617 result = merge(a: type, b: result);
618
619 // If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
620 // we better not change the type.
621 if (!canPrimitivelyConvertFromTo(from: tracked, to: result)
622 && !canPopulate(type: result, argument: tracked, isExtension: nullptr)
623 && !selectConstructor(type: result, argument: tracked, isExtension: nullptr).isValid()) {
624 return false;
625 }
626
627 it->replacement = comparableType(type: result);
628 *mutableTracked = std::move(*QQmlJSScope::clone(origin: result));
629 return true;
630}
631
632void QQmlJSTypeResolver::adjustOriginalType(
633 const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const
634{
635 if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
636 return;
637
638 const auto it = m_trackedTypes->find(key: tracked);
639 Q_ASSERT(it != m_trackedTypes->end());
640
641 it->original = conversion;
642 *it->clone = std::move(*QQmlJSScope::clone(origin: conversion));
643}
644
645void QQmlJSTypeResolver::generalizeType(const QQmlJSScope::ConstPtr &type) const
646{
647 if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
648 return;
649
650 const auto it = m_trackedTypes->find(key: type);
651 Q_ASSERT(it != m_trackedTypes->end());
652 *it->clone = std::move(*QQmlJSScope::clone(origin: genericType(type)));
653 if (it->replacement)
654 it->replacement = genericType(type: it->replacement);
655 it->original = genericType(type: it->original);
656}
657
658QString QQmlJSTypeResolver::containedTypeName(const QQmlJSRegisterContent &container,
659 bool useFancyName) const
660{
661 QQmlJSScope::ConstPtr type;
662
663 // Use the type proper instead of the attached type
664 switch (container.variant()) {
665 case QQmlJSRegisterContent::ScopeAttached:
666 case QQmlJSRegisterContent::MetaType:
667 type = container.scopeType();
668 break;
669 default:
670 type = containedType(container);
671 break;
672 }
673
674 QString typeName = type->internalName().isEmpty() ? type->baseTypeName() : type->internalName();
675
676 if (useFancyName)
677 return QQmlJSScope::prettyName(name: typeName);
678
679 return typeName;
680}
681
682bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSScope::ConstPtr &from,
683 const QQmlJSScope::ConstPtr &to) const
684{
685 if (canPrimitivelyConvertFromTo(from, to)
686 || canPopulate(type: to, argument: from, isExtension: nullptr)
687 || selectConstructor(type: to, argument: from, isExtension: nullptr).isValid()) {
688 return true;
689 }
690
691 // ### need a generic solution for custom cpp types:
692 // if (from->m_hasBoolOverload && equals(to, boolType))
693 // return true;
694
695 // All of these types have QString conversions that require a certain format
696 // TODO: Actually verify these strings or deprecate them.
697 // Some of those type are builtins or should be builtins. We should add code for them
698 // in QQmlJSCodeGenerator::conversion().
699 if (equals(a: from, b: m_stringType) && !to.isNull()) {
700 const QString toTypeName = to->internalName();
701 if (toTypeName == u"QPoint"_s || toTypeName == u"QPointF"_s
702 || toTypeName == u"QSize"_s || toTypeName == u"QSizeF"_s
703 || toTypeName == u"QRect"_s || toTypeName == u"QRectF"_s) {
704 return true;
705 }
706 }
707
708 return false;
709}
710
711bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSRegisterContent &from,
712 const QQmlJSRegisterContent &to) const
713{
714 return canConvertFromTo(from: containedType(container: from), to: containedType(container: to));
715}
716
717static QQmlJSRegisterContent::ContentVariant mergeVariants(QQmlJSRegisterContent::ContentVariant a,
718 QQmlJSRegisterContent::ContentVariant b)
719{
720 return (a == b) ? a : QQmlJSRegisterContent::Unknown;
721}
722
723QQmlJSRegisterContent QQmlJSTypeResolver::merge(const QQmlJSRegisterContent &a,
724 const QQmlJSRegisterContent &b) const
725{
726 if (a == b)
727 return a;
728
729 QList<QQmlJSScope::ConstPtr> origins;
730
731 QQmlJSScope::ConstPtr aResultScope;
732 if (a.isConversion()) {
733 origins.append(other: a.conversionOrigins());
734 aResultScope = a.conversionResultScope();
735 } else {
736 origins.append(t: containedType(container: a));
737 aResultScope = a.scopeType();
738 }
739
740 QQmlJSScope::ConstPtr bResultScope;
741 if (b.isConversion()) {
742 origins.append(other: b.conversionOrigins());
743 bResultScope = b.conversionResultScope();
744 } else {
745 origins.append(t: containedType(container: b));
746 bResultScope = b.scopeType();
747 }
748
749 std::sort(first: origins.begin(), last: origins.end());
750 const auto erase = std::unique(first: origins.begin(), last: origins.end());
751 origins.erase(abegin: erase, aend: origins.end());
752
753 return QQmlJSRegisterContent::create(
754 storedType: merge(a: a.storedType(), b: b.storedType()),
755 origins,
756 conversion: merge(a: containedType(container: a), b: containedType(container: b)),
757 conversionScope: merge(a: aResultScope, b: bResultScope),
758 variant: mergeVariants(a: a.variant(), b: b.variant()),
759 scope: merge(a: a.scopeType(), b: b.scopeType()));
760}
761
762QQmlJSScope::ConstPtr QQmlJSTypeResolver::merge(const QQmlJSScope::ConstPtr &a,
763 const QQmlJSScope::ConstPtr &b) const
764{
765 if (a.isNull())
766 return b;
767
768 if (b.isNull())
769 return a;
770
771 const auto commonBaseType = [this](
772 const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) {
773 for (QQmlJSScope::ConstPtr aBase = a; aBase; aBase = aBase->baseType()) {
774 for (QQmlJSScope::ConstPtr bBase = b; bBase; bBase = bBase->baseType()) {
775 if (equals(a: aBase, b: bBase))
776 return aBase;
777 }
778 }
779
780 return QQmlJSScope::ConstPtr();
781 };
782
783
784 if (equals(a, b))
785 return a;
786
787 if (equals(a, b: jsValueType()) || equals(a, b: varType()))
788 return a;
789 if (equals(a: b, b: jsValueType()) || equals(a: b, b: varType()))
790 return b;
791
792 const auto isInt32Compatible = [&](const QQmlJSScope::ConstPtr &type) {
793 return (isIntegral(type) && !equals(a: type, b: uint32Type())) || equals(a: type, b: boolType());
794 };
795
796 if (isInt32Compatible(a) && isInt32Compatible(b))
797 return int32Type();
798
799 const auto isUInt32Compatible = [&](const QQmlJSScope::ConstPtr &type) {
800 return isUnsignedInteger(type) || equals(a: type, b: boolType());
801 };
802
803 if (isUInt32Compatible(a) && isUInt32Compatible(b))
804 return uint32Type();
805
806 if (isNumeric(type: a) && isNumeric(type: b))
807 return realType();
808
809 if (isPrimitive(type: a) && isPrimitive(type: b))
810 return jsPrimitiveType();
811
812 if (auto commonBase = commonBaseType(a, b))
813 return commonBase;
814
815 if ((equals(a, b: nullType()) || equals(a, b: boolType())) && b->isReferenceType())
816 return b;
817
818 if ((equals(a: b, b: nullType()) || equals(a: b, b: boolType())) && a->isReferenceType())
819 return a;
820
821 return varType();
822}
823
824bool QQmlJSTypeResolver::canHold(
825 const QQmlJSScope::ConstPtr &container, const QQmlJSScope::ConstPtr &contained) const
826{
827 if (equals(a: container, b: contained)
828 || equals(a: container, b: m_varType)
829 || equals(a: container, b: m_jsValueType)) {
830 return true;
831 }
832
833 if (equals(a: container, b: m_jsPrimitiveType))
834 return isPrimitive(type: contained);
835
836 if (equals(a: container, b: m_variantListType))
837 return contained->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
838
839 if (equals(a: container, b: m_qObjectListType) || equals(a: container, b: m_listPropertyType)) {
840 if (contained->accessSemantics() != QQmlJSScope::AccessSemantics::Sequence)
841 return false;
842 if (QQmlJSScope::ConstPtr value = contained->valueType())
843 return value->isReferenceType();
844 return false;
845 }
846
847 if (QQmlJSUtils::searchBaseAndExtensionTypes(
848 type: container, check: [&](const QQmlJSScope::ConstPtr &base) {
849 return equals(a: base, b: contained);
850 })) {
851 return true;
852 }
853
854 if (container->isReferenceType()) {
855 if (QQmlJSUtils::searchBaseAndExtensionTypes(
856 type: contained, check: [&](const QQmlJSScope::ConstPtr &base) {
857 return equals(a: base, b: container);
858 })) {
859 return true;
860 }
861 }
862
863 return false;
864}
865
866
867bool QQmlJSTypeResolver::canHoldUndefined(const QQmlJSRegisterContent &content) const
868{
869 const auto canBeUndefined = [this](const QQmlJSScope::ConstPtr &type) {
870 return equals(a: type, b: m_voidType) || equals(a: type, b: m_varType)
871 || equals(a: type, b: m_jsValueType) || equals(a: type, b: m_jsPrimitiveType);
872 };
873
874 if (!canBeUndefined(content.storedType()))
875 return false;
876
877 if (!content.isConversion())
878 return canBeUndefined(containedType(container: content));
879
880 const auto origins = content.conversionOrigins();
881 for (const auto &origin : origins) {
882 if (canBeUndefined(origin))
883 return true;
884 }
885
886 return false;
887}
888
889bool QQmlJSTypeResolver::isOptionalType(const QQmlJSRegisterContent &content) const
890{
891 if (!content.isConversion())
892 return false;
893
894 const auto origins = content.conversionOrigins();
895 if (origins.length() != 2)
896 return false;
897
898 return equals(a: origins[0], b: m_voidType) || equals(a: origins[1], b: m_voidType);
899}
900
901QQmlJSScope::ConstPtr QQmlJSTypeResolver::extractNonVoidFromOptionalType(
902 const QQmlJSRegisterContent &content) const
903{
904 if (!isOptionalType(content))
905 return QQmlJSScope::ConstPtr();
906
907 const auto origins = content.conversionOrigins();
908 const QQmlJSScope::ConstPtr result = equals(a: origins[0], b: m_voidType) ? origins[1] : origins[0];
909 Q_ASSERT(!equals(result, m_voidType));
910 return result;
911}
912
913QQmlJSScope::ConstPtr QQmlJSTypeResolver::genericType(
914 const QQmlJSScope::ConstPtr &type,
915 ComponentIsGeneric allowComponent) const
916{
917 if (type->isScript())
918 return m_jsValueType;
919
920 if (equals(a: type, b: m_metaObjectType))
921 return m_metaObjectType;
922
923 if (type->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
924 QString unresolvedBaseTypeName;
925 for (auto base = type; base;) {
926 // QObject and QQmlComponent are the two required base types.
927 // Any QML type system has to define those, or use the ones from builtins.
928 // As QQmlComponent is derived from QObject, we can restrict ourselves to the latter.
929 // This results in less if'ery when retrieving a QObject* from somewhere and deciding
930 // what it is.
931 if (base->internalName() == u"QObject"_s) {
932 return base;
933 } else if (allowComponent == ComponentIsGeneric::Yes
934 && base->internalName() == u"QQmlComponent"_s) {
935 return base;
936 }
937
938 if (auto baseBase = base->baseType()) {
939 base = baseBase;
940 } else {
941 unresolvedBaseTypeName = base->baseTypeName();
942 break;
943 }
944 }
945
946 // Reference types that are not QObject or QQmlComponent are likely JavaScript objects.
947 // We don't want to deal with those, but m_jsValueType is the best generic option.
948 if (type->filePath().isEmpty())
949 return m_jsValueType;
950
951 m_logger->log(message: u"Object type %1 is not derived from QObject or QQmlComponent. "
952 "You may need to fully qualify all names in C++ so that moc can see them. "
953 "You may also need to add qt_extract_metatypes(<target containing %2>)."_s
954 .arg(args: type->internalName(), args&: unresolvedBaseTypeName),
955 id: qmlCompiler, srcLocation: type->sourceLocation());
956
957 // If it does have a filePath, it's some C++ type which we haven't fully resolved.
958 return m_jsValueType;
959 }
960
961 if (type->isListProperty())
962 return m_listPropertyType;
963
964 if (type->scopeType() == QQmlSA::ScopeType::EnumScope)
965 return type->baseType();
966
967 if (isPrimitive(type))
968 return type;
969
970 for (const QQmlJSScope::ConstPtr &builtin : {
971 m_realType, m_floatType, m_int8Type, m_uint8Type, m_int16Type, m_uint16Type,
972 m_int32Type, m_uint32Type, m_int64Type, m_uint64Type, m_boolType, m_stringType,
973 m_stringListType, m_byteArrayType, m_urlType, m_dateTimeType, m_dateType,
974 m_timeType, m_variantListType, m_variantMapType, m_varType, m_jsValueType,
975 m_jsPrimitiveType, m_listPropertyType, m_qObjectType, m_qObjectListType,
976 m_metaObjectType, m_forInIteratorPtr, m_forOfIteratorPtr }) {
977 if (equals(a: type, b: builtin) || equals(a: type, b: builtin->listType()))
978 return type;
979 }
980
981 return m_varType;
982}
983
984QQmlJSRegisterContent QQmlJSTypeResolver::builtinType(const QQmlJSScope::ConstPtr &type) const
985{
986 Q_ASSERT(storedType(type) == type);
987 return QQmlJSRegisterContent::create(
988 storedType: type, type, resultLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex, variant: QQmlJSRegisterContent::Builtin);
989}
990
991QQmlJSRegisterContent QQmlJSTypeResolver::globalType(const QQmlJSScope::ConstPtr &type) const
992{
993 return QQmlJSRegisterContent::create(
994 storedType: storedType(type), type, resultLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex,
995 variant: QQmlJSRegisterContent::Unknown);
996}
997
998static QQmlJSRegisterContent::ContentVariant scopeContentVariant(QQmlJSScope::ExtensionKind mode,
999 bool isMethod)
1000{
1001 switch (mode) {
1002 case QQmlJSScope::NotExtension:
1003 return isMethod ? QQmlJSRegisterContent::ScopeMethod : QQmlJSRegisterContent::ScopeProperty;
1004 case QQmlJSScope::ExtensionType:
1005 case QQmlJSScope::ExtensionJavaScript:
1006 return isMethod ? QQmlJSRegisterContent::ExtensionScopeMethod
1007 : QQmlJSRegisterContent::ExtensionScopeProperty;
1008 case QQmlJSScope::ExtensionNamespace:
1009 break;
1010 }
1011 Q_UNREACHABLE_RETURN(QQmlJSRegisterContent::Unknown);
1012}
1013
1014static bool isRevisionAllowed(int memberRevision, const QQmlJSScope::ConstPtr &scope)
1015{
1016 Q_ASSERT(scope->isComposite());
1017 const QTypeRevision revision = QTypeRevision::fromEncodedVersion(value: memberRevision);
1018
1019 // If the memberRevision is either invalid or 0.0, then everything is allowed.
1020 if (!revision.isValid() || revision == QTypeRevision::zero())
1021 return true;
1022
1023 const QTypeRevision typeRevision = QQmlJSScope::nonCompositeBaseRevision(
1024 scope: {.scope: scope->baseType(), .revision: scope->baseTypeRevision()});
1025
1026 // If the revision is not valid, we haven't found a non-composite base type.
1027 // There is nothing we can say about the property then.
1028 return typeRevision.isValid() && typeRevision >= revision;
1029}
1030
1031QQmlJSRegisterContent QQmlJSTypeResolver::scopedType(const QQmlJSScope::ConstPtr &scope,
1032 const QString &name, int lookupIndex,
1033 QQmlJSScopesByIdOptions options) const
1034{
1035 const auto isAssignedToDefaultProperty = [this](const QQmlJSScope::ConstPtr &parent,
1036 const QQmlJSScope::ConstPtr &child) {
1037 const QString defaultPropertyName = parent->defaultPropertyName();
1038 if (defaultPropertyName.isEmpty()) // no reason to search for bindings
1039 return false;
1040
1041 const QList<QQmlJSMetaPropertyBinding> defaultPropBindings =
1042 parent->propertyBindings(name: defaultPropertyName);
1043 for (const QQmlJSMetaPropertyBinding &binding : defaultPropBindings) {
1044 if (binding.bindingType() == QQmlSA::BindingType::Object
1045 && equals(a: binding.objectType(), b: child)) {
1046 return true;
1047 }
1048 }
1049 return false;
1050 };
1051
1052 if (QQmlJSScope::ConstPtr identified = m_objectsById.scope(id: name, referrer: scope, options)) {
1053 return QQmlJSRegisterContent::create(storedType: storedType(type: identified), type: identified, resultLookupIndex: lookupIndex,
1054 variant: QQmlJSRegisterContent::ObjectById, scope);
1055 }
1056
1057 if (QQmlJSScope::ConstPtr base = QQmlJSScope::findCurrentQMLScope(scope)) {
1058 QQmlJSRegisterContent result;
1059 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1060 type: base, check: [&](const QQmlJSScope::ConstPtr &found, QQmlJSScope::ExtensionKind mode) {
1061 if (mode == QQmlJSScope::ExtensionNamespace) // no use for it here
1062 return false;
1063 if (found->hasOwnProperty(name)) {
1064 QQmlJSMetaProperty prop = found->ownProperty(name);
1065 if (!isRevisionAllowed(memberRevision: prop.revision(), scope))
1066 return false;
1067 if (m_parentMode == UseDocumentParent
1068 && name == base->parentPropertyName()) {
1069 QQmlJSScope::ConstPtr baseParent = base->parentScope();
1070 if (baseParent && baseParent->inherits(base: prop.type())
1071 && isAssignedToDefaultProperty(baseParent, base)) {
1072 prop.setType(baseParent);
1073 }
1074 }
1075 result = QQmlJSRegisterContent::create(
1076 storedType: storedType(type: prop.type()), property: prop,
1077 baseLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex, resultLookupIndex: lookupIndex,
1078 variant: scopeContentVariant(mode, isMethod: false), scope);
1079 return true;
1080 }
1081
1082 if (found->hasOwnMethod(name)) {
1083 auto methods = found->ownMethods(name);
1084 for (auto it = methods.begin(); it != methods.end();) {
1085 if (!isRevisionAllowed(memberRevision: it->revision(), scope))
1086 it = methods.erase(pos: it);
1087 else
1088 ++it;
1089 }
1090 if (methods.isEmpty())
1091 return false;
1092 result = QQmlJSRegisterContent::create(
1093 storedType: jsValueType(), methods, variant: scopeContentVariant(mode, isMethod: true), scope);
1094 return true;
1095 }
1096
1097 // Unqualified enums are not allowed
1098
1099 return false;
1100 })) {
1101 return result;
1102 }
1103 }
1104
1105 QQmlJSRegisterContent result = registerContentForName(name);
1106
1107 if (result.isValid())
1108 return result;
1109
1110 if (m_jsGlobalObject->hasProperty(name)) {
1111 return QQmlJSRegisterContent::create(
1112 storedType: jsValueType(), property: m_jsGlobalObject->property(name),
1113 baseLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex, resultLookupIndex: lookupIndex,
1114 variant: QQmlJSRegisterContent::JavaScriptGlobal, scope: m_jsGlobalObject);
1115 } else if (m_jsGlobalObject->hasMethod(name)) {
1116 return QQmlJSRegisterContent::create(storedType: jsValueType(), methods: m_jsGlobalObject->methods(name),
1117 variant: QQmlJSRegisterContent::JavaScriptGlobal,
1118 scope: m_jsGlobalObject);
1119 }
1120
1121 return {};
1122}
1123
1124bool QQmlJSTypeResolver::checkEnums(const QQmlJSScope::ConstPtr &scope, const QString &name,
1125 QQmlJSRegisterContent *result,
1126 QQmlJSScope::ExtensionKind mode) const
1127{
1128 // You can't have lower case enum names in QML, even if we know the enums here.
1129 if (name.isEmpty() || !name.at(i: 0).isUpper())
1130 return false;
1131
1132 const bool inExtension = (mode != QQmlJSScope::NotExtension);
1133
1134 const auto enums = scope->ownEnumerations();
1135 for (const auto &enumeration : enums) {
1136 if ((enumeration.isScoped() || enumeration.isQml()) && enumeration.name() == name) {
1137 *result = QQmlJSRegisterContent::create(
1138 storedType: storedType(type: enumeration.type()), enumeration, enumMember: QString(),
1139 variant: inExtension ? QQmlJSRegisterContent::ExtensionObjectEnum
1140 : QQmlJSRegisterContent::ObjectEnum,
1141 scope);
1142 return true;
1143 }
1144
1145 if ((!enumeration.isScoped() || enumeration.isQml() || !scope->enforcesScopedEnums())
1146 && enumeration.hasKey(key: name)) {
1147 *result = QQmlJSRegisterContent::create(
1148 storedType: storedType(type: enumeration.type()), enumeration, enumMember: name,
1149 variant: inExtension ? QQmlJSRegisterContent::ExtensionObjectEnum
1150 : QQmlJSRegisterContent::ObjectEnum,
1151 scope);
1152 return true;
1153 }
1154 }
1155
1156 return false;
1157}
1158
1159bool QQmlJSTypeResolver::canPopulate(
1160 const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &passedArgumentType,
1161 bool *isExtension) const
1162{
1163 // TODO: We could allow QVariantMap and QVariantHash to be populated, but that needs extra
1164 // code in the code generator.
1165
1166 if (type.isNull()
1167 || canHold(container: passedArgumentType, contained: type)
1168 || isPrimitive(type: passedArgumentType)
1169 || type->accessSemantics() != QQmlJSScope::AccessSemantics::Value
1170 || !type->isStructured()) {
1171 return false;
1172 }
1173
1174 if (isExtension)
1175 *isExtension = !type->extensionType().scope.isNull();
1176
1177 return true;
1178}
1179
1180QQmlJSMetaMethod QQmlJSTypeResolver::selectConstructor(
1181 const QQmlJSScope::ConstPtr &type, const QQmlJSScope::ConstPtr &passedArgumentType,
1182 bool *isExtension) const
1183{
1184 // If the "from" type can hold the target type, we should not try to coerce
1185 // it to any constructor argument.
1186 if (type.isNull()
1187 || canHold(container: passedArgumentType, contained: type)
1188 || type->accessSemantics() != QQmlJSScope::AccessSemantics::Value
1189 || !type->isCreatable()) {
1190 return QQmlJSMetaMethod();
1191 }
1192
1193 auto doSelectConstructor = [&](const QQmlJSScope::ConstPtr &type) {
1194 QQmlJSMetaMethod candidate;
1195
1196 const auto ownMethods = type->ownMethods();
1197 for (const QQmlJSMetaMethod &method : ownMethods) {
1198 if (!method.isConstructor())
1199 continue;
1200
1201 const auto index = method.constructorIndex();
1202 Q_ASSERT(index != QQmlJSMetaMethod::RelativeFunctionIndex::Invalid);
1203
1204 const auto methodArguments = method.parameters();
1205 if (methodArguments.size() != 1)
1206 continue;
1207
1208 const QQmlJSScope::ConstPtr methodArgumentType = methodArguments[0].type();
1209
1210 if (equals(a: passedArgumentType, b: methodArgumentType))
1211 return method;
1212
1213 // Do not select further ctors here. We don't want to do multi-step construction as that
1214 // is confusing and easily leads to infinite recursion.
1215 if (!candidate.isValid()
1216 && canPrimitivelyConvertFromTo(from: passedArgumentType, to: methodArgumentType)) {
1217 candidate = method;
1218 }
1219 }
1220
1221 return candidate;
1222 };
1223
1224 if (QQmlJSScope::ConstPtr extension = type->extensionType().scope) {
1225 const QQmlJSMetaMethod ctor = doSelectConstructor(extension);
1226 if (ctor.isValid()) {
1227 if (isExtension)
1228 *isExtension = true;
1229 return ctor;
1230 }
1231 }
1232
1233 if (isExtension)
1234 *isExtension = false;
1235
1236 return doSelectConstructor(type);
1237}
1238
1239bool QQmlJSTypeResolver::areEquivalentLists(
1240 const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
1241{
1242 const QQmlJSScope::ConstPtr equivalentLists[2][2] = {
1243 { m_stringListType, m_stringType->listType() },
1244 { m_variantListType, m_varType->listType() }
1245 };
1246
1247 for (const auto eq : equivalentLists) {
1248 if ((equals(a, b: eq[0]) && equals(a: b, b: eq[1])) || (equals(a, b: eq[1]) && equals(a: b, b: eq[0])))
1249 return true;
1250 }
1251
1252 return false;
1253}
1254
1255bool QQmlJSTypeResolver::isTriviallyCopyable(const QQmlJSScope::ConstPtr &type) const
1256{
1257 // pointers are trivially copyable
1258 if (type->isReferenceType())
1259 return true;
1260
1261 // Enum values are trivially copyable
1262 if (type->scopeType() == QQmlSA::ScopeType::EnumScope)
1263 return true;
1264
1265 for (const QQmlJSScope::ConstPtr &trivial : {
1266 m_nullType, m_voidType,
1267 m_boolType, m_metaObjectType,
1268 m_realType, m_floatType,
1269 m_int8Type, m_uint8Type,
1270 m_int16Type, m_uint16Type,
1271 m_int32Type, m_uint32Type,
1272 m_int64Type, m_uint64Type }) {
1273 if (equals(a: type, b: trivial))
1274 return true;
1275 }
1276
1277 return false;
1278}
1279
1280bool QQmlJSTypeResolver::inherits(const QQmlJSScope::ConstPtr &derived, const QQmlJSScope::ConstPtr &base) const
1281{
1282 const bool matchByName = !base->isComposite();
1283 for (QQmlJSScope::ConstPtr derivedBase = derived; derivedBase;
1284 derivedBase = derivedBase->baseType()) {
1285 if (equals(a: derivedBase, b: base))
1286 return true;
1287 if (matchByName
1288 && !derivedBase->isComposite()
1289 && derivedBase->internalName() == base->internalName()) {
1290 return true;
1291 }
1292 }
1293 return false;
1294}
1295
1296bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
1297 const QQmlJSScope::ConstPtr &from, const QQmlJSScope::ConstPtr &to) const
1298{
1299 if (equals(a: from, b: to))
1300 return true;
1301 if (equals(a: from, b: m_varType) || equals(a: to, b: m_varType))
1302 return true;
1303 if (equals(a: from, b: m_jsValueType) || equals(a: to, b: m_jsValueType))
1304 return true;
1305 if (equals(a: to, b: m_qQmlScriptStringType))
1306 return true;
1307 if (isNumeric(type: from) && isNumeric(type: to))
1308 return true;
1309 if (isNumeric(type: from) && equals(a: to, b: m_boolType))
1310 return true;
1311 if (from->accessSemantics() == QQmlJSScope::AccessSemantics::Reference
1312 && (equals(a: to, b: m_boolType) || equals(a: to, b: m_stringType))) {
1313 return true;
1314 }
1315
1316 // Yes, our String has number constructors.
1317 if (isNumeric(type: from) && equals(a: to, b: m_stringType))
1318 return true;
1319
1320 // We can convert strings to numbers, but not to enums
1321 if (equals(a: from, b: m_stringType) && isNumeric(type: to))
1322 return to->scopeType() != QQmlJSScope::ScopeType::EnumScope;
1323
1324 // We can always convert between strings and urls.
1325 if ((equals(a: from, b: m_stringType) && equals(a: to, b: m_urlType))
1326 || (equals(a: from, b: m_urlType) && equals(a: to, b: m_stringType))) {
1327 return true;
1328 }
1329
1330 // We can always convert between strings and byte arrays.
1331 if ((equals(a: from, b: m_stringType) && equals(a: to, b: m_byteArrayType))
1332 || (equals(a: from, b: m_byteArrayType) && equals(a: to, b: m_stringType))) {
1333 return true;
1334 }
1335
1336 if (equals(a: to, b: m_voidType))
1337 return true;
1338
1339 if (to.isNull())
1340 return equals(a: from, b: m_voidType);
1341
1342 const auto types = { m_dateTimeType, m_dateType, m_timeType, m_stringType };
1343 for (const auto &originType : types) {
1344 if (!equals(a: from, b: originType))
1345 continue;
1346
1347 for (const auto &targetType : types) {
1348 if (equals(a: to, b: targetType))
1349 return true;
1350 }
1351
1352 if (equals(a: to, b: m_realType))
1353 return true;
1354
1355 break;
1356 }
1357
1358 if (equals(a: from, b: m_nullType)
1359 && to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
1360 return true;
1361 }
1362
1363 if (equals(a: from, b: m_jsPrimitiveType)) {
1364 // You can cast any primitive to a nullptr
1365 return isPrimitive(type: to) || to->accessSemantics() == QQmlJSScope::AccessSemantics::Reference;
1366 }
1367
1368 if (equals(a: to, b: m_jsPrimitiveType))
1369 return isPrimitive(type: from);
1370
1371 if (equals(a: from, b: m_variantListType))
1372 return to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence;
1373
1374 const bool matchByName = !to->isComposite();
1375 Q_ASSERT(!matchByName || !to->internalName().isEmpty());
1376 for (auto baseType = from; baseType; baseType = baseType->baseType()) {
1377 if (equals(a: baseType, b: to))
1378 return true;
1379 if (matchByName && baseType->internalName() == to->internalName())
1380 return true;
1381 }
1382
1383 // We can convert anything that fits into QJSPrimitiveValue
1384 if (canConvertFromTo(from, to: m_jsPrimitiveType) && canConvertFromTo(from: m_jsPrimitiveType, to))
1385 return true;
1386
1387 // We can convert everything to bool.
1388 if (equals(a: to, b: m_boolType))
1389 return true;
1390
1391 if (areEquivalentLists(a: from, b: to))
1392 return true;
1393
1394 if (from->isListProperty()
1395 && to->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence
1396 && canConvertFromTo(from: from->valueType(), to: to->valueType())) {
1397 return true;
1398 }
1399
1400 if (equals(a: to, b: m_stringType)
1401 && from->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence) {
1402 return canConvertFromTo(from: from->valueType(), to: m_stringType);
1403 }
1404
1405 return false;
1406}
1407
1408QQmlJSRegisterContent QQmlJSTypeResolver::lengthProperty(
1409 bool isWritable, const QQmlJSScope::ConstPtr &scope) const
1410{
1411 QQmlJSMetaProperty prop;
1412 prop.setPropertyName(u"length"_s);
1413 prop.setTypeName(u"qsizetype"_s);
1414 prop.setType(sizeType());
1415 prop.setIsWritable(isWritable);
1416 return QQmlJSRegisterContent::create(
1417 storedType: sizeType(), property: prop, baseLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex,
1418 resultLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex, variant: QQmlJSRegisterContent::Builtin, scope);
1419}
1420
1421QQmlJSRegisterContent QQmlJSTypeResolver::memberType(
1422 const QQmlJSScope::ConstPtr &type, const QString &name, int baseLookupIndex,
1423 int resultLookupIndex) const
1424{
1425 QQmlJSRegisterContent result;
1426
1427 // If we got a plain type reference we have to check the enums of the _scope_.
1428 if (equals(a: type, b: metaObjectType()))
1429 return {};
1430
1431 if (equals(a: type, b: variantMapType())) {
1432 QQmlJSMetaProperty prop;
1433 prop.setPropertyName(name);
1434 prop.setTypeName(u"QVariant"_s);
1435 prop.setType(varType());
1436 prop.setIsWritable(true);
1437 return QQmlJSRegisterContent::create(
1438 storedType: varType(), property: prop, baseLookupIndex, resultLookupIndex,
1439 variant: QQmlJSRegisterContent::GenericObjectProperty, scope: type);
1440 }
1441
1442 if (equals(a: type, b: jsValueType())) {
1443 QQmlJSMetaProperty prop;
1444 prop.setPropertyName(name);
1445 prop.setTypeName(u"QJSValue"_s);
1446 prop.setType(jsValueType());
1447 prop.setIsWritable(true);
1448 return QQmlJSRegisterContent::create(
1449 storedType: jsValueType(), property: prop, baseLookupIndex, resultLookupIndex,
1450 variant: QQmlJSRegisterContent::GenericObjectProperty, scope: type);
1451 }
1452
1453 if ((equals(a: type, b: stringType())
1454 || type->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
1455 && name == u"length"_s) {
1456 return lengthProperty(isWritable: !equals(a: type, b: stringType()), scope: type);
1457 }
1458
1459 const auto check = [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
1460 if (mode != QQmlJSScope::ExtensionNamespace) {
1461 if (scope->hasOwnProperty(name)) {
1462 const auto prop = scope->ownProperty(name);
1463 result = QQmlJSRegisterContent::create(
1464 storedType: storedType(type: prop.type()),
1465 property: prop, baseLookupIndex, resultLookupIndex,
1466 variant: mode == QQmlJSScope::NotExtension
1467 ? QQmlJSRegisterContent::ObjectProperty
1468 : QQmlJSRegisterContent::ExtensionObjectProperty,
1469 scope);
1470 return true;
1471 }
1472
1473 if (scope->hasOwnMethod(name)) {
1474 const auto methods = scope->ownMethods(name);
1475 result = QQmlJSRegisterContent::create(
1476 storedType: jsValueType(), methods,
1477 variant: mode == QQmlJSScope::NotExtension
1478 ? QQmlJSRegisterContent::ObjectMethod
1479 : QQmlJSRegisterContent::ExtensionObjectMethod,
1480 scope);
1481 return true;
1482 }
1483 }
1484
1485 return checkEnums(scope, name, result: &result, mode);
1486 };
1487
1488 if (QQmlJSUtils::searchBaseAndExtensionTypes(type, check))
1489 return result;
1490
1491 for (auto scope = type;
1492 scope && (scope->scopeType() == QQmlSA::ScopeType::JSFunctionScope
1493 || scope->scopeType() == QQmlSA::ScopeType::JSLexicalScope);
1494 scope = scope->parentScope()) {
1495 if (auto ownIdentifier = scope->ownJSIdentifier(id: name)) {
1496 QQmlJSMetaProperty prop;
1497 prop.setPropertyName(name);
1498 prop.setTypeName(u"QJSValue"_s);
1499 prop.setType(jsValueType());
1500 prop.setIsWritable(!(ownIdentifier.value().isConst));
1501
1502 return QQmlJSRegisterContent::create(storedType: jsValueType(), property: prop, baseLookupIndex,
1503 resultLookupIndex,
1504 variant: QQmlJSRegisterContent::JavaScriptObject, scope);
1505 }
1506 }
1507
1508 if (QQmlJSScope::ConstPtr attachedBase = typeForName(name)) {
1509 if (QQmlJSScope::ConstPtr attached = attachedBase->attachedType()) {
1510 if (!genericType(type: attached)) {
1511 m_logger->log(message: u"Cannot resolve generic base of attached %1"_s.arg(
1512 a: attached->internalName()),
1513 id: qmlCompiler, srcLocation: attached->sourceLocation());
1514 return {};
1515 } else if (type->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
1516 m_logger->log(message: u"Cannot retrieve attached object for non-reference type %1"_s.arg(
1517 a: type->internalName()),
1518 id: qmlCompiler, srcLocation: type->sourceLocation());
1519 return {};
1520 } else {
1521 return QQmlJSRegisterContent::create(
1522 storedType: storedType(type: attached), type: attached, resultLookupIndex,
1523 variant: QQmlJSRegisterContent::ObjectAttached, scope: attachedBase);
1524 }
1525 }
1526 }
1527
1528 return {};
1529}
1530
1531QQmlJSRegisterContent QQmlJSTypeResolver::memberEnumType(const QQmlJSScope::ConstPtr &type,
1532 const QString &name) const
1533{
1534 QQmlJSRegisterContent result;
1535
1536 if (QQmlJSUtils::searchBaseAndExtensionTypes(
1537 type, check: [&](const QQmlJSScope::ConstPtr &scope, QQmlJSScope::ExtensionKind mode) {
1538 return checkEnums(scope, name, result: &result, mode);
1539 })) {
1540 return result;
1541 }
1542
1543 return {};
1544}
1545
1546QQmlJSRegisterContent QQmlJSTypeResolver::memberType(
1547 const QQmlJSRegisterContent &type, const QString &name, int lookupIndex) const
1548{
1549 if (type.isType()) {
1550 const auto content = type.type();
1551 const auto result = memberType(type: content, name, baseLookupIndex: type.resultLookupIndex(), resultLookupIndex: lookupIndex);
1552 if (result.isValid())
1553 return result;
1554
1555 // If we didn't find anything and it's an attached type,
1556 // we might have an enum of the attaching type.
1557 return memberEnumType(type: type.scopeType(), name);
1558 }
1559 if (type.isProperty())
1560 return memberType(type: type.property().type(), name, baseLookupIndex: type.resultLookupIndex(), resultLookupIndex: lookupIndex);
1561 if (type.isEnumeration()) {
1562 const auto enumeration = type.enumeration();
1563 if (!type.enumMember().isEmpty() || !enumeration.hasKey(key: name))
1564 return {};
1565 return QQmlJSRegisterContent::create(storedType: storedType(type: enumeration.type()), enumeration, enumMember: name,
1566 variant: QQmlJSRegisterContent::ObjectEnum, scope: type.scopeType());
1567 }
1568 if (type.isMethod()) {
1569 QQmlJSMetaProperty prop;
1570 prop.setTypeName(u"QJSValue"_s);
1571 prop.setPropertyName(name);
1572 prop.setType(jsValueType());
1573 prop.setIsWritable(true);
1574 return QQmlJSRegisterContent::create(
1575 storedType: jsValueType(), property: prop, baseLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex, resultLookupIndex: lookupIndex,
1576 variant: QQmlJSRegisterContent::GenericObjectProperty, scope: jsValueType());
1577 }
1578 if (type.isImportNamespace()) {
1579 if (type.scopeType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
1580 m_logger->log(message: u"Cannot use a non-QObject type %1 to access prefixed import"_s.arg(
1581 a: type.scopeType()->internalName()),
1582 id: qmlPrefixedImportType, srcLocation: type.scopeType()->sourceLocation());
1583 return {};
1584 }
1585
1586 return registerContentForName(
1587 name, scopeType: type.scopeType(),
1588 hasObjectModulePrefix: type.variant() == QQmlJSRegisterContent::ObjectModulePrefix);
1589 }
1590 if (type.isConversion()) {
1591 if (const auto result = memberType(
1592 type: type.conversionResult(), name, baseLookupIndex: type.resultLookupIndex(), resultLookupIndex: lookupIndex);
1593 result.isValid()) {
1594 return result;
1595 }
1596
1597 if (const auto result = memberEnumType(type: type.scopeType(), name); result.isValid())
1598 return result;
1599
1600 // If the conversion consists of only undefined and one actual type,
1601 // we can produce the members of that one type.
1602 // If the value is then actually undefined, the result is an exception.
1603
1604 auto origins = type.conversionOrigins();
1605 const auto begin = origins.begin();
1606 const auto end = std::remove_if(first: begin, last: origins.end(),
1607 pred: [this](const QQmlJSScope::ConstPtr &origin) {
1608 return equals(a: origin, b: m_voidType);
1609 });
1610
1611 // If the conversion cannot hold the original type, it loses information.
1612 return (end - begin == 1 && canHold(container: type.conversionResult(), contained: *begin))
1613 ? memberType(type: *begin, name, baseLookupIndex: type.resultLookupIndex(), resultLookupIndex: lookupIndex)
1614 : QQmlJSRegisterContent();
1615 }
1616
1617 Q_UNREACHABLE_RETURN({});
1618}
1619
1620QQmlJSRegisterContent QQmlJSTypeResolver::valueType(const QQmlJSRegisterContent &list) const
1621{
1622 QQmlJSScope::ConstPtr scope;
1623 QQmlJSScope::ConstPtr value;
1624
1625 auto valueType = [&](const QQmlJSScope::ConstPtr &scope) {
1626 if (scope->accessSemantics() == QQmlJSScope::AccessSemantics::Sequence)
1627 return scope->valueType();
1628
1629 if (equals(a: scope, b: m_forInIteratorPtr))
1630 return m_sizeType;
1631
1632 if (equals(a: scope, b: m_forOfIteratorPtr))
1633 return list.scopeType()->valueType();
1634
1635 if (equals(a: scope, b: m_jsValueType) || equals(a: scope, b: m_varType))
1636 return m_jsValueType;
1637
1638 if (equals(a: scope, b: m_stringType))
1639 return m_stringType;
1640
1641 return QQmlJSScope::ConstPtr();
1642 };
1643
1644 if (list.isType()) {
1645 scope = list.type();
1646 value = valueType(scope);
1647 } else if (list.isConversion()) {
1648 value = valueType(list.conversionResult());
1649 } else if (list.isProperty()) {
1650 const auto prop = list.property();
1651 scope = prop.type();
1652 value = valueType(scope);
1653 }
1654
1655 if (value.isNull())
1656 return {};
1657
1658 QQmlJSMetaProperty property;
1659 property.setPropertyName(u"[]"_s);
1660 property.setTypeName(value->internalName());
1661 property.setType(value);
1662
1663 return QQmlJSRegisterContent::create(
1664 storedType: storedType(type: value), property, baseLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex,
1665 resultLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex, variant: QQmlJSRegisterContent::ListValue, scope);
1666}
1667
1668QQmlJSRegisterContent QQmlJSTypeResolver::returnType(
1669 const QQmlJSScope::ConstPtr &type, QQmlJSRegisterContent::ContentVariant variant,
1670 const QQmlJSScope::ConstPtr &scope) const
1671{
1672 Q_ASSERT(variant == QQmlJSRegisterContent::MethodReturnValue
1673 || variant == QQmlJSRegisterContent::JavaScriptReturnValue);
1674 return QQmlJSRegisterContent::create(
1675 storedType: storedType(type), type, resultLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex, variant, scope);
1676}
1677
1678QQmlJSRegisterContent QQmlJSTypeResolver::iteratorPointer(
1679 const QQmlJSRegisterContent &listType, QQmlJS::AST::ForEachType type,
1680 int lookupIndex) const
1681{
1682 const QQmlJSScope::ConstPtr value = (type == QQmlJS::AST::ForEachType::In)
1683 ? m_int32Type
1684 : containedType(container: valueType(list: listType));
1685
1686 QQmlJSScope::ConstPtr iteratorPointer = type == QQmlJS::AST::ForEachType::In
1687 ? m_forInIteratorPtr
1688 : m_forOfIteratorPtr;
1689
1690 const QQmlJSScope::ConstPtr listContained = containedType(container: listType);
1691
1692 QQmlJSMetaProperty prop;
1693 prop.setPropertyName(u"<>"_s);
1694 prop.setTypeName(iteratorPointer->internalName());
1695 prop.setType(iteratorPointer);
1696 return QQmlJSRegisterContent::create(
1697 storedType: storedType(type: iteratorPointer), property: prop, baseLookupIndex: lookupIndex,
1698 resultLookupIndex: QQmlJSRegisterContent::InvalidLookupIndex, variant: QQmlJSRegisterContent::ListIterator,
1699 scope: listContained);
1700}
1701
1702bool QQmlJSTypeResolver::registerIsStoredIn(
1703 const QQmlJSRegisterContent &reg, const QQmlJSScope::ConstPtr &type) const
1704{
1705 return equals(a: reg.storedType(), b: type);
1706}
1707
1708bool QQmlJSTypeResolver::registerContains(const QQmlJSRegisterContent &reg,
1709 const QQmlJSScope::ConstPtr &type) const
1710{
1711 if (reg.isType())
1712 return equals(a: reg.type(), b: type);
1713 if (reg.isConversion())
1714 return equals(a: reg.conversionResult(), b: type);
1715 if (reg.isProperty())
1716 return equals(a: type, b: reg.property().type());
1717 if (reg.isEnumeration())
1718 return equals(a: type, b: reg.enumeration().type());
1719 if (reg.isMethod())
1720 return equals(a: type, b: jsValueType());
1721 return false;
1722}
1723
1724QQmlJSScope::ConstPtr QQmlJSTypeResolver::storedType(const QQmlJSScope::ConstPtr &type) const
1725{
1726 if (type.isNull())
1727 return {};
1728 if (equals(a: type, b: voidType()))
1729 return type;
1730 if (type->isScript())
1731 return jsValueType();
1732 if (type->isComposite()) {
1733 if (const QQmlJSScope::ConstPtr nonComposite = QQmlJSScope::nonCompositeBaseType(type))
1734 return nonComposite;
1735
1736 // If we can't find the non-composite base, we really don't know what it is.
1737 return genericType(type);
1738 }
1739 if (type->filePath().isEmpty())
1740 return genericType(type);
1741 return type;
1742}
1743
1744QQmlJSScope::ConstPtr QQmlJSTypeResolver::originalType(const QQmlJSScope::ConstPtr &type) const
1745{
1746 const auto it = m_trackedTypes->find(key: type);
1747 return it == m_trackedTypes->end() ? type : it->original;
1748}
1749
1750/*!
1751 * \internal
1752 *
1753 * Compares the origin types of \a a and \a b. A straight a == b would compare the identity
1754 * of the pointers. However, since we clone types to keep track of them, we need a separate
1755 * way to compare the clones. Usually you'd do *a == *b for that, but as QQmlJSScope is rather
1756 * large, we offer an optimization here that uses the type tracking we already have in place.
1757 */
1758bool QQmlJSTypeResolver::equals(const QQmlJSScope::ConstPtr &a, const QQmlJSScope::ConstPtr &b) const
1759{
1760 return comparableType(type: a) == comparableType(type: b);
1761}
1762
1763QQmlJSRegisterContent QQmlJSTypeResolver::convert(
1764 const QQmlJSRegisterContent &from, const QQmlJSRegisterContent &to) const
1765{
1766 if (from.isConversion()) {
1767 return QQmlJSRegisterContent::create(
1768 storedType: to.storedType(), origins: from.conversionOrigins(), conversion: containedType(container: to),
1769 conversionScope: to.scopeType() ? to.scopeType() : from.conversionResultScope(),
1770 variant: from.variant(), scope: from.scopeType());
1771 }
1772
1773 return QQmlJSRegisterContent::create(
1774 storedType: to.storedType(), origins: QList<QQmlJSScope::ConstPtr>{containedType(container: from)},
1775 conversion: containedType(container: to), conversionScope: to.scopeType(), variant: from.variant(), scope: from.scopeType());
1776}
1777
1778QQmlJSRegisterContent QQmlJSTypeResolver::cast(
1779 const QQmlJSRegisterContent &from, const QQmlJSScope::ConstPtr &to) const
1780{
1781 return from.castTo(newContainedType: to).storedIn(newStoredType: storedType(type: to));
1782}
1783
1784QQmlJSScope::ConstPtr QQmlJSTypeResolver::comparableType(const QQmlJSScope::ConstPtr &type) const
1785{
1786 const auto it = m_trackedTypes->constFind(key: type);
1787 if (it == m_trackedTypes->constEnd())
1788 return type;
1789 return it->replacement ? it->replacement : it->original;
1790}
1791
1792QT_END_NAMESPACE
1793

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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