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