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